You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by as...@apache.org on 2021/11/10 07:52:45 UTC

[camel-k] branch main updated: feat: Health trait

This is an automated email from the ASF dual-hosted git repository.

astefanutti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/main by this push:
     new 38df070  feat: Health trait
38df070 is described below

commit 38df070ec3d95992caf9a563a138026c44d79654
Author: Antonin Stefanutti <an...@stefanutti.fr>
AuthorDate: Tue Nov 9 13:16:10 2021 +0100

    feat: Health trait
---
 docs/modules/ROOT/nav.adoc               |   1 +
 docs/modules/traits/pages/container.adoc |  16 ++-
 docs/modules/traits/pages/health.adoc    |  88 +++++++++++++
 e2e/knative/knative_platform_test.go     |   2 +-
 pkg/resources/resources.go               |   6 +-
 pkg/trait/container.go                   | 156 ++++++----------------
 pkg/trait/container_probes_test.go       | 213 ++++++++++++++-----------------
 pkg/trait/container_test.go              |  40 ++++--
 pkg/trait/health.go                      | 195 ++++++++++++++++++++++++++++
 pkg/trait/jolokia.go                     |   2 +-
 pkg/trait/jvm.go                         |   2 +-
 pkg/trait/jvm_test.go                    |   2 +-
 pkg/trait/knative_service.go             |   4 +-
 pkg/trait/prometheus.go                  |   2 +-
 pkg/trait/quarkus.go                     |   2 +-
 pkg/trait/trait_register.go              |   1 +
 pkg/trait/trait_types.go                 |   4 +-
 resources/traits.yaml                    | 116 +++++++++++++----
 18 files changed, 568 insertions(+), 284 deletions(-)

diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 375ad93..223a98e 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -53,6 +53,7 @@
 ** xref:traits:environment.adoc[Environment]
 ** xref:traits:error-handler.adoc[Error Handler]
 ** xref:traits:gc.adoc[Gc]
+** xref:traits:health.adoc[Health]
 ** xref:traits:ingress.adoc[Ingress]
 ** xref:traits:istio.adoc[Istio]
 ** xref:traits:jolokia.adoc[Jolokia]
diff --git a/docs/modules/traits/pages/container.adoc b/docs/modules/traits/pages/container.adoc
index ac5124c..fa48ab3 100755
--- a/docs/modules/traits/pages/container.adoc
+++ b/docs/modules/traits/pages/container.adoc
@@ -5,7 +5,6 @@ The Container trait can be used to configure properties of the container where t
 
 It also provides configuration for Services associated to the container.
 
-
 This trait is available in the following profiles: **Kubernetes, Knative, OpenShift**.
 
 WARNING: The container trait is a *platform trait*: disabling it may compromise the platform functionality.
@@ -83,59 +82,72 @@ The following configuration options are available:
 
 | container.probes-enabled
 | bool
-| ProbesEnabled enable/disable probes on the container (default `false`)
+| DeprecatedProbesEnabled enable/disable probes on the container (default `false`)
+Deprecated: replaced by the health trait.
 
 | container.liveness-scheme
 | string
 | Scheme to use when connecting. Defaults to HTTP. Applies to the liveness probe.
+Deprecated: replaced by the health trait.
 
 | container.liveness-initial-delay
 | int32
 | Number of seconds after the container has started before liveness probes are initiated.
+Deprecated: replaced by the health trait.
 
 | container.liveness-timeout
 | int32
 | Number of seconds after which the probe times out. Applies to the liveness probe.
+Deprecated: replaced by the health trait.
 
 | container.liveness-period
 | int32
 | How often to perform the probe. Applies to the liveness probe.
+Deprecated: replaced by the health trait.
 
 | container.liveness-success-threshold
 | int32
 | Minimum consecutive successes for the probe to be considered successful after having failed.
 Applies to the liveness probe.
+Deprecated: replaced by the health trait.
 
 | container.liveness-failure-threshold
 | int32
 | Minimum consecutive failures for the probe to be considered failed after having succeeded.
 Applies to the liveness probe.
+Deprecated: replaced by the health trait.
 
 | container.readiness-scheme
 | string
 | Scheme to use when connecting. Defaults to HTTP. Applies to the readiness probe.
+Deprecated: replaced by the health trait.
 
 | container.readiness-initial-delay
 | int32
 | Number of seconds after the container has started before readiness probes are initiated.
+Deprecated: replaced by the health trait.
 
 | container.readiness-timeout
 | int32
 | Number of seconds after which the probe times out. Applies to the readiness probe.
+Deprecated: replaced by the health trait.
 
 | container.readiness-period
 | int32
 | How often to perform the probe. Applies to the readiness probe.
+Deprecated: replaced by the health trait.
 
 | container.readiness-success-threshold
 | int32
 | Minimum consecutive successes for the probe to be considered successful after having failed.
 Applies to the readiness probe.
+Deprecated: replaced by the health trait.
 
 | container.readiness-failure-threshold
 | int32
 | Minimum consecutive failures for the probe to be considered failed after having succeeded.
 Applies to the readiness probe.
+Deprecated: replaced by the health trait.
 
 |===
 
diff --git a/docs/modules/traits/pages/health.adoc b/docs/modules/traits/pages/health.adoc
new file mode 100755
index 0000000..6606de9
--- /dev/null
+++ b/docs/modules/traits/pages/health.adoc
@@ -0,0 +1,88 @@
+= Health Trait
+
+// Start of autogenerated code - DO NOT EDIT! (description)
+The health trait is responsible for configuring the health probes on the integration container.
+
+It's disabled by default.
+
+
+This trait is available in the following profiles: **Kubernetes, Knative, OpenShift**.
+
+// End of autogenerated code - DO NOT EDIT! (description)
+// Start of autogenerated code - DO NOT EDIT! (configuration)
+== Configuration
+
+Trait properties can be specified when running any integration with the CLI:
+[source,console]
+----
+$ kamel run --trait health.[key]=[value] --trait health.[key2]=[value2] integration.groovy
+----
+The following configuration options are available:
+
+[cols="2m,1m,5a"]
+|===
+|Property | Type | Description
+
+| health.enabled
+| bool
+| Can be used to enable or disable a trait. All traits share this common property.
+
+| health.liveness-probe-enabled
+| bool
+| Configures the liveness probe for the integration container (default `false`).
+
+| health.liveness-scheme
+| string
+| Scheme to use when connecting to the liveness probe (default `HTTP`).
+
+| health.liveness-initial-delay
+| int32
+| Number of seconds after the container has started before the liveness probe is initiated.
+
+| health.liveness-timeout
+| int32
+| Number of seconds after which the liveness probe times out.
+
+| health.liveness-period
+| int32
+| How often to perform the liveness probe.
+
+| health.liveness-success-threshold
+| int32
+| Minimum consecutive successes for the liveness probe to be considered successful after having failed.
+
+| health.liveness-failure-threshold
+| int32
+| Minimum consecutive failures for the liveness probe to be considered failed after having succeeded.
+
+| health.readiness-probe-enabled
+| bool
+| Configures the readiness probe for the integration container (default `true`).
+
+| health.readiness-scheme
+| string
+| Scheme to use when connecting to the readiness probe (default `HTTP`).
+
+| health.readiness-initial-delay
+| int32
+| Number of seconds after the container has started before the readiness probe is initiated.
+
+| health.readiness-timeout
+| int32
+| Number of seconds after which the readiness probe times out.
+
+| health.readiness-period
+| int32
+| How often to perform the readiness probe.
+
+| health.readiness-success-threshold
+| int32
+| Minimum consecutive successes for the readiness probe to be considered successful after having failed.
+
+| health.readiness-failure-threshold
+| int32
+| Minimum consecutive failures for the readiness probe to be considered failed after having succeeded.
+
+|===
+
+// End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/e2e/knative/knative_platform_test.go b/e2e/knative/knative_platform_test.go
index 8a6f280..f75a690 100644
--- a/e2e/knative/knative_platform_test.go
+++ b/e2e/knative/knative_platform_test.go
@@ -71,7 +71,7 @@ func TestKnativePlatform(t *testing.T) {
 			Eventually(IntegrationPhase(ns, "yaml")).Should(Equal(v1.IntegrationPhaseRunning))
 			Eventually(IntegrationLogs(ns, "yaml"), TestTimeoutShort).Should(ContainSubstring("Magicstring!!!"))
 			// It should keep the old profile saved in status
-			Eventually(IntegrationProfile(ns, "yaml"), TestTimeoutMedium).Should(Equal(v1.TraitProfile(string(cluster))))
+			Eventually(IntegrationProfile(ns, "yaml"), TestTimeoutMedium).Should(Equal(v1.TraitProfile(cluster)))
 
 			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
 		})
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index 55dfc34..b8af443 100644
--- a/pkg/resources/resources.go
+++ b/pkg/resources/resources.go
@@ -534,14 +534,14 @@ var assets = func() http.FileSystem {
 			modTime:          time.Time{},
 			uncompressedSize: 89871,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x7d\x4b\x77\xdb\x38\xb6\xee\xdc\xbf\x82\xab\x32\x39\x67\xdd\x16\xba\x2a\xd5\xf7\xd4\x5d\x75\x47\xb6\x1c\x27\x76\x6c\xc7\x89\xdc\x49\xba\x27\xb5\x20\x12\x92\x60\x91\x04\x0d\x80\xb2\x9c\x5f\x7f\x16\x40\xf0\x29\x65\xf3\xe1\x0d\xb7\x06\xe2\x03\x1b\xdf\xc6\xfe\xf0\x20\xde\x78\x13\xcc\xf0\x7e\x27\x6f\x82\x6b\x1e\xb2\x54\xb1\x28\xd0\x22\xd0\x1b\x16\x9c\x66\x34\xdc\xb0\x60\x21\x56\xfa\x89\x4a\x16\x5c\x88\x3c\x8d\xa8\xe6\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x7d\x4b\x77\xdb\x38\xb6\xee\xdc\xbf\x82\xab\x32\x39\x67\xdd\x16\xba\x2a\xd5\xf7\xd4\x5d\x75\x47\xb6\x1c\x27\x76\x6c\xc7\x89\xdc\x49\xba\x27\xb5\x20\x12\x92\x60\x91\x04\x0d\x80\xb2\x9c\x5f\x7f\x16\x40\xf0\x29\x65\xf3\xe1\x0d\xb7\x06\xe2\x03\x1b\xdf\xc6\xfe\xf0\x20\xde\x78\x13\xcc\xf0\x7e\x27\x6f\x82\x6b\x1e\xb2\x54\xb1\x28\xd0\x22\xd0\x1b\x16\x9c\x66\x34\xdc\xb0\x60\x21\x56\xfa\x89\x4a\x16\x5c\x88\x3c\x8d\xa8\xe6\x [...]
 		},
 		"/traits.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "traits.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 43798,
+			uncompressedSize: 46832,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\x7d\x73\x1c\xb7\xd1\x20\xfe\xbf\x3e\x05\x8a\xcf\xaf\x4a\x24\x6b\x77\x49\x3b\x4f\x12\xff\x78\xa7\x4b\xd1\x92\x9c\xd0\xd6\x0b\x4f\x92\x9d\x4b\xf9\x5c\x59\xec\x4c\xef\x2e\xb4\x33\xc0\x04\xc0\x90\xda\xdc\x73\xdf\xfd\x0a\xdd\x8d\x97\xd9\x5d\x92\x4b\x59\xf4\x85\x57\x4f\xf2\x87\x45\x72\x00\x34\x1a\x8d\x7e\xef\x86\xb7\x52\x79\x77\xf6\x64\x2c\xb4\x6c\xe1\x4c\xc8\xf9\x5c\x69\xe5\xd7\x4f\x84\xe8\x1a\xe9\xe7\xc6\xb6\x67\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfd\x73\x1c\xb7\xb1\xe0\xef\xfe\x2b\x50\x7c\x57\x25\x92\xb5\xbb\x94\x9d\x97\x3c\x1f\xef\x74\x29\x5a\x92\x13\xda\xfa\xe0\x49\xb2\x73\x29\x9f\x2b\x8b\x9d\xe9\xdd\x85\x38\x0b\x4c\x00\x0c\xa9\xcd\xbd\xfb\xdf\xaf\xd0\xdd\xf8\x98\xd9\x5d\x72\x29\x91\x7e\xe1\xd5\x4b\x7e\xb0\x48\x0e\x80\x46\xa3\xd1\xdf\xdd\xf0\x56\x2a\xef\x4e\xbf\x1a\x0b\x2d\x57\x70\x2a\xe4\x7c\xae\xb4\xf2\xeb\xaf\x84\x68\x1b\xe9\xe7\xc6\xae\x4e\xc5\x [...]
 		},
 	}
 	fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
diff --git a/pkg/trait/container.go b/pkg/trait/container.go
index 26f2b99..a12b261 100644
--- a/pkg/trait/container.go
+++ b/pkg/trait/container.go
@@ -20,9 +20,7 @@ package trait
 import (
 	"fmt"
 	"path"
-	"sort"
 
-	"github.com/apache/camel-k/pkg/util/defaults"
 	appsv1 "k8s.io/api/apps/v1"
 	"k8s.io/api/batch/v1beta1"
 	corev1 "k8s.io/api/core/v1"
@@ -33,6 +31,7 @@ import (
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/util"
+	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/envvar"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
@@ -42,7 +41,6 @@ const (
 	defaultContainerPort     = 8080
 	defaultContainerPortName = "http"
 	defaultServicePort       = 80
-	defaultProbePath         = "/q/health"
 	containerTraitID         = "container"
 )
 
@@ -82,48 +80,62 @@ type containerTrait struct {
 	Image string `property:"image" json:"image,omitempty"`
 	// The pull policy: Always|Never|IfNotPresent
 	ImagePullPolicy corev1.PullPolicy `property:"image-pull-policy" json:"imagePullPolicy,omitempty"`
-	// ProbesEnabled enable/disable probes on the container (default `false`)
-	ProbesEnabled *bool `property:"probes-enabled" json:"probesEnabled,omitempty"`
+
+	// DeprecatedProbesEnabled enable/disable probes on the container (default `false`)
+	// Deprecated: replaced by the health trait.
+	DeprecatedProbesEnabled *bool `property:"probes-enabled" json:"probesEnabled,omitempty"`
 	// Scheme to use when connecting. Defaults to HTTP. Applies to the liveness probe.
-	LivenessScheme string `property:"liveness-scheme" json:"livenessScheme,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedLivenessScheme string `property:"liveness-scheme" json:"livenessScheme,omitempty"`
 	// Number of seconds after the container has started before liveness probes are initiated.
-	LivenessInitialDelay int32 `property:"liveness-initial-delay" json:"livenessInitialDelay,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedLivenessInitialDelay int32 `property:"liveness-initial-delay" json:"livenessInitialDelay,omitempty"`
 	// Number of seconds after which the probe times out. Applies to the liveness probe.
-	LivenessTimeout int32 `property:"liveness-timeout" json:"livenessTimeout,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedLivenessTimeout int32 `property:"liveness-timeout" json:"livenessTimeout,omitempty"`
 	// How often to perform the probe. Applies to the liveness probe.
-	LivenessPeriod int32 `property:"liveness-period" json:"livenessPeriod,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedLivenessPeriod int32 `property:"liveness-period" json:"livenessPeriod,omitempty"`
 	// Minimum consecutive successes for the probe to be considered successful after having failed.
 	// Applies to the liveness probe.
-	LivenessSuccessThreshold int32 `property:"liveness-success-threshold" json:"livenessSuccessThreshold,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedLivenessSuccessThreshold int32 `property:"liveness-success-threshold" json:"livenessSuccessThreshold,omitempty"`
 	// Minimum consecutive failures for the probe to be considered failed after having succeeded.
 	// Applies to the liveness probe.
-	LivenessFailureThreshold int32 `property:"liveness-failure-threshold" json:"livenessFailureThreshold,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedLivenessFailureThreshold int32 `property:"liveness-failure-threshold" json:"livenessFailureThreshold,omitempty"`
 	// Scheme to use when connecting. Defaults to HTTP. Applies to the readiness probe.
-	ReadinessScheme string `property:"readiness-scheme" json:"readinessScheme,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedReadinessScheme string `property:"readiness-scheme" json:"readinessScheme,omitempty"`
 	// Number of seconds after the container has started before readiness probes are initiated.
-	ReadinessInitialDelay int32 `property:"readiness-initial-delay" json:"readinessInitialDelay,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedReadinessInitialDelay int32 `property:"readiness-initial-delay" json:"readinessInitialDelay,omitempty"`
 	// Number of seconds after which the probe times out. Applies to the readiness probe.
-	ReadinessTimeout int32 `property:"readiness-timeout" json:"readinessTimeout,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedReadinessTimeout int32 `property:"readiness-timeout" json:"readinessTimeout,omitempty"`
 	// How often to perform the probe. Applies to the readiness probe.
-	ReadinessPeriod int32 `property:"readiness-period" json:"readinessPeriod,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedReadinessPeriod int32 `property:"readiness-period" json:"readinessPeriod,omitempty"`
 	// Minimum consecutive successes for the probe to be considered successful after having failed.
 	// Applies to the readiness probe.
-	ReadinessSuccessThreshold int32 `property:"readiness-success-threshold" json:"readinessSuccessThreshold,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedReadinessSuccessThreshold int32 `property:"readiness-success-threshold" json:"readinessSuccessThreshold,omitempty"`
 	// Minimum consecutive failures for the probe to be considered failed after having succeeded.
 	// Applies to the readiness probe.
-	ReadinessFailureThreshold int32 `property:"readiness-failure-threshold" json:"readinessFailureThreshold,omitempty"`
+	// Deprecated: replaced by the health trait.
+	DeprecatedReadinessFailureThreshold int32 `property:"readiness-failure-threshold" json:"readinessFailureThreshold,omitempty"`
 }
 
 func newContainerTrait() Trait {
 	return &containerTrait{
-		BaseTrait:       NewBaseTrait(containerTraitID, 1600),
-		Port:            defaultContainerPort,
-		ServicePort:     defaultServicePort,
-		ServicePortName: defaultContainerPortName,
-		Name:            defaultContainerName,
-		ProbesEnabled:   BoolP(false),
-		LivenessScheme:  string(corev1.URISchemeHTTP),
-		ReadinessScheme: string(corev1.URISchemeHTTP),
+		BaseTrait:                 NewBaseTrait(containerTraitID, 1600),
+		Port:                      defaultContainerPort,
+		ServicePort:               defaultServicePort,
+		ServicePortName:           defaultContainerPortName,
+		Name:                      defaultContainerName,
+		DeprecatedProbesEnabled:   BoolP(false),
+		DeprecatedLivenessScheme:  string(corev1.URISchemeHTTP),
+		DeprecatedReadinessScheme: string(corev1.URISchemeHTTP),
 	}
 }
 
@@ -156,18 +168,9 @@ func isValidPullPolicy(policy corev1.PullPolicy) bool {
 
 func (t *containerTrait) Apply(e *Environment) error {
 	if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
-		if err := t.configureDependencies(e); err != nil {
-			return err
-		}
-	} else {
-		return t.configureContainer(e)
+		return t.configureImageIntegrationKit(e)
 	}
-
-	if err := t.configureImageIntegrationKit(e); err != nil {
-		return err
-	}
-
-	return nil
+	return t.configureContainer(e)
 }
 
 // IsPlatformTrait overrides base class method
@@ -175,21 +178,6 @@ func (t *containerTrait) IsPlatformTrait() bool {
 	return true
 }
 
-func (t *containerTrait) configureDependencies(e *Environment) error {
-	if IsTrue(t.ProbesEnabled) {
-		if capability, ok := e.CamelCatalog.Runtime.Capabilities[v1.CapabilityHealth]; ok {
-			for _, dependency := range capability.Dependencies {
-				util.StringSliceUniqueAdd(&e.Integration.Status.Dependencies, dependency.GetDependencyID())
-			}
-
-			// sort the dependencies to get always the same list if they don't change
-			sort.Strings(e.Integration.Status.Dependencies)
-		}
-	}
-
-	return nil
-}
-
 func (t *containerTrait) configureImageIntegrationKit(e *Environment) error {
 	if t.Image != "" {
 		if e.Integration.Spec.IntegrationKit != nil {
@@ -269,16 +257,8 @@ func (t *containerTrait) configureContainer(e *Environment) error {
 	}
 	t.configureCapabilities(e)
 
-	portName := t.PortName
-	if portName == "" {
-		portName = defaultContainerPortName
-	}
 	// Deployment
 	if err := e.Resources.VisitDeploymentE(func(deployment *appsv1.Deployment) error {
-		if IsTrue(t.ProbesEnabled) && portName == defaultContainerPortName {
-			t.configureProbes(&container, t.Port, defaultProbePath)
-		}
-
 		for _, envVar := range e.EnvVars {
 			envvar.SetVar(&container.Env, envVar)
 		}
@@ -302,11 +282,6 @@ func (t *containerTrait) configureContainer(e *Environment) error {
 
 	// Knative Service
 	if err := e.Resources.VisitKnativeServiceE(func(service *serving.Service) error {
-		if IsTrue(t.ProbesEnabled) && portName == defaultContainerPortName {
-			// don't set the port on Knative service as it is not allowed.
-			t.configureProbes(&container, 0, defaultProbePath)
-		}
-
 		for _, env := range e.EnvVars {
 			switch {
 			case env.ValueFrom == nil:
@@ -341,10 +316,6 @@ func (t *containerTrait) configureContainer(e *Environment) error {
 
 	// CronJob
 	if err := e.Resources.VisitCronJobE(func(cron *v1beta1.CronJob) error {
-		if IsTrue(t.ProbesEnabled) && portName == defaultContainerPortName {
-			t.configureProbes(&container, t.Port, defaultProbePath)
-		}
-
 		for _, envVar := range e.EnvVars {
 			envvar.SetVar(&container.Env, envVar)
 		}
@@ -462,52 +433,3 @@ func (t *containerTrait) configureCapabilities(e *Environment) {
 		e.ApplicationProperties["camel.context.rest-configuration.component"] = "platform-http"
 	}
 }
-
-func (t *containerTrait) configureProbes(container *corev1.Container, port int, path string) {
-	container.LivenessProbe = t.newLivenessProbe(port, path)
-	container.ReadinessProbe = t.newReadinessProbe(port, path)
-}
-
-func (t *containerTrait) newLivenessProbe(port int, path string) *corev1.Probe {
-	action := corev1.HTTPGetAction{}
-	action.Path = path
-	action.Scheme = corev1.URIScheme(t.LivenessScheme)
-
-	if port > 0 {
-		action.Port = intstr.FromInt(port)
-	}
-
-	p := corev1.Probe{
-		Handler: corev1.Handler{
-			HTTPGet: &action,
-		},
-	}
-
-	p.InitialDelaySeconds = t.LivenessInitialDelay
-	p.TimeoutSeconds = t.LivenessTimeout
-	p.PeriodSeconds = t.LivenessPeriod
-	p.SuccessThreshold = t.LivenessSuccessThreshold
-	p.FailureThreshold = t.LivenessFailureThreshold
-
-	return &p
-}
-
-func (t *containerTrait) newReadinessProbe(port int, path string) *corev1.Probe {
-	p := corev1.Probe{
-		Handler: corev1.Handler{
-			HTTPGet: &corev1.HTTPGetAction{
-				Port:   intstr.FromInt(port),
-				Path:   path,
-				Scheme: corev1.URIScheme(t.ReadinessScheme),
-			},
-		},
-	}
-
-	p.InitialDelaySeconds = t.ReadinessInitialDelay
-	p.TimeoutSeconds = t.ReadinessTimeout
-	p.PeriodSeconds = t.ReadinessPeriod
-	p.SuccessThreshold = t.ReadinessSuccessThreshold
-	p.FailureThreshold = t.ReadinessFailureThreshold
-
-	return &p
-}
diff --git a/pkg/trait/container_probes_test.go b/pkg/trait/container_probes_test.go
index cf9ba13..0d4ccbd 100644
--- a/pkg/trait/container_probes_test.go
+++ b/pkg/trait/container_probes_test.go
@@ -22,170 +22,149 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	serving "knative.dev/serving/pkg/apis/serving/v1"
-
-	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/util/camel"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
-// nolint: unparam
-func newTestProbesEnv(t *testing.T, provider v1.RuntimeProvider) Environment {
+func newTestProbesEnv(t *testing.T, integration *v1.Integration) Environment {
 	t.Helper()
 
-	var catalog *camel.RuntimeCatalog
-	var err error
-
-	switch provider {
-	case v1.RuntimeProviderQuarkus:
-		catalog, err = camel.QuarkusCatalog()
-	default:
-		panic("unknown provider " + provider)
-	}
-
+	catalog, err := camel.DefaultCatalog()
 	assert.Nil(t, err)
 	assert.NotNil(t, catalog)
 
+	traitCatalog := NewCatalog(nil)
+
 	return Environment{
-		CamelCatalog: catalog,
-		Integration: &v1.Integration{
-			Status: v1.IntegrationStatus{},
-		},
+		Catalog:               traitCatalog,
+		CamelCatalog:          catalog,
+		Platform:              &v1.IntegrationPlatform{},
+		Integration:           integration,
 		Resources:             kubernetes.NewCollection(),
 		ApplicationProperties: make(map[string]string),
 	}
 }
 
-func newTestContainerTrait() *containerTrait {
-	tr, _ := newContainerTrait().(*containerTrait)
-	tr.ProbesEnabled = BoolP(true)
-
-	return tr
-}
+func TestProbesDependencies(t *testing.T) {
+	integration := &v1.Integration{
+		Spec: v1.IntegrationSpec{
+			Traits: map[string]v1.TraitSpec{
+				"container": test.TraitSpecFromMap(t, map[string]interface{}{
+					"probesEnabled": true,
+				}),
+			},
+		},
+	}
 
-func TestProbesDepsQuarkus(t *testing.T) {
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
+	env := newTestProbesEnv(t, integration)
 	env.Integration.Status.Phase = v1.IntegrationPhaseInitialization
 
-	ctr := newTestContainerTrait()
-
-	ok, err := ctr.Configure(&env)
+	err := env.Catalog.apply(&env)
 	assert.Nil(t, err)
-	assert.True(t, ok)
 
-	err = ctr.Apply(&env)
-	assert.Nil(t, err)
 	assert.Contains(t, env.Integration.Status.Dependencies, "mvn:org.apache.camel.quarkus:camel-quarkus-microprofile-health")
 }
 
 func TestProbesOnDeployment(t *testing.T) {
-	target := appsv1.Deployment{}
+	integration := &v1.Integration{
+		Spec: v1.IntegrationSpec{
+			Traits: map[string]v1.TraitSpec{
+				"container": test.TraitSpecFromMap(t, map[string]interface{}{
+					"probesEnabled":   true,
+					"expose":          true,
+					"LivenessTimeout": 1234,
+				}),
+			},
+		},
+	}
 
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
+	env := newTestProbesEnv(t, integration)
 	env.Integration.Status.Phase = v1.IntegrationPhaseDeploying
-	env.Resources.Add(&target)
-
-	ctr := newTestContainerTrait()
-	ctr.Expose = BoolP(true)
-	ctr.LivenessTimeout = 1234
 
-	err := ctr.Apply(&env)
+	err := env.Catalog.apply(&env)
 	assert.Nil(t, err)
 
-	assert.Equal(t, "", target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Host)
-	assert.Equal(t, int32(defaultContainerPort), target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Port.IntVal)
-	assert.Equal(t, defaultProbePath, target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Path)
-	assert.Equal(t, corev1.URISchemeHTTP, target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Scheme)
-	assert.Equal(t, "", target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Host)
-	assert.Equal(t, int32(defaultContainerPort), target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Port.IntVal)
-	assert.Equal(t, defaultProbePath, target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Path)
-	assert.Equal(t, corev1.URISchemeHTTP, target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Scheme)
-	assert.Equal(t, int32(1234), target.Spec.Template.Spec.Containers[0].LivenessProbe.TimeoutSeconds)
+	container := env.GetIntegrationContainer()
+
+	assert.Equal(t, "", container.LivenessProbe.HTTPGet.Host)
+	assert.Equal(t, int32(defaultContainerPort), container.LivenessProbe.HTTPGet.Port.IntVal)
+	assert.Equal(t, defaultLivenessProbePath, container.LivenessProbe.HTTPGet.Path)
+	assert.Equal(t, corev1.URISchemeHTTP, container.ReadinessProbe.HTTPGet.Scheme)
+	assert.Equal(t, "", container.ReadinessProbe.HTTPGet.Host)
+	assert.Equal(t, int32(defaultContainerPort), container.ReadinessProbe.HTTPGet.Port.IntVal)
+	assert.Equal(t, defaultReadinessProbePath, container.ReadinessProbe.HTTPGet.Path)
+	assert.Equal(t, corev1.URISchemeHTTP, container.LivenessProbe.HTTPGet.Scheme)
+	assert.Equal(t, int32(1234), container.LivenessProbe.TimeoutSeconds)
 }
 
 func TestProbesOnDeploymentWithCustomScheme(t *testing.T) {
-	target := appsv1.Deployment{}
+	integration := &v1.Integration{
+		Spec: v1.IntegrationSpec{
+			Traits: map[string]v1.TraitSpec{
+				"container": test.TraitSpecFromMap(t, map[string]interface{}{
+					"probesEnabled":   true,
+					"expose":          true,
+					"livenessTimeout": 1234,
+					"livenessScheme":  "HTTPS",
+					"readinessScheme": "HTTPS",
+				}),
+			},
+		},
+	}
 
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
+	env := newTestProbesEnv(t, integration)
 	env.Integration.Status.Phase = v1.IntegrationPhaseDeploying
-	env.Resources.Add(&target)
 
-	ctr := newTestContainerTrait()
-	ctr.Expose = BoolP(true)
-	ctr.LivenessTimeout = 1234
-	ctr.LivenessScheme = "HTTPS"
-	ctr.ReadinessScheme = "HTTPS"
-
-	err := ctr.Apply(&env)
+	err := env.Catalog.apply(&env)
 	assert.Nil(t, err)
 
-	assert.Equal(t, "", target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Host)
-	assert.Equal(t, int32(defaultContainerPort), target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Port.IntVal)
-	assert.Equal(t, defaultProbePath, target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Path)
-	assert.Equal(t, corev1.URISchemeHTTPS, target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Scheme)
-	assert.Equal(t, "", target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Host)
-	assert.Equal(t, int32(defaultContainerPort), target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Port.IntVal)
-	assert.Equal(t, defaultProbePath, target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Path)
-	assert.Equal(t, corev1.URISchemeHTTPS, target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Scheme)
-	assert.Equal(t, int32(1234), target.Spec.Template.Spec.Containers[0].LivenessProbe.TimeoutSeconds)
-}
-
-func TestProbesOnDeploymentWithNoHttpPort(t *testing.T) {
-	target := appsv1.Deployment{}
-
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
-	env.Integration.Status.Phase = v1.IntegrationPhaseDeploying
-	env.Resources.Add(&target)
-
-	ctr := newTestContainerTrait()
-	ctr.PortName = "custom"
-	ctr.LivenessTimeout = 1234
-
-	err := ctr.Apply(&env)
-	assert.Nil(t, err)
-	assert.Nil(t, target.Spec.Template.Spec.Containers[0].LivenessProbe)
-	assert.Nil(t, target.Spec.Template.Spec.Containers[0].ReadinessProbe)
+	container := env.GetIntegrationContainer()
+
+	assert.Equal(t, "", container.LivenessProbe.HTTPGet.Host)
+	assert.Equal(t, int32(defaultContainerPort), container.LivenessProbe.HTTPGet.Port.IntVal)
+	assert.Equal(t, defaultLivenessProbePath, container.LivenessProbe.HTTPGet.Path)
+	assert.Equal(t, corev1.URISchemeHTTPS, container.ReadinessProbe.HTTPGet.Scheme)
+	assert.Equal(t, "", container.ReadinessProbe.HTTPGet.Host)
+	assert.Equal(t, int32(defaultContainerPort), container.ReadinessProbe.HTTPGet.Port.IntVal)
+	assert.Equal(t, defaultReadinessProbePath, container.ReadinessProbe.HTTPGet.Path)
+	assert.Equal(t, corev1.URISchemeHTTPS, container.LivenessProbe.HTTPGet.Scheme)
+	assert.Equal(t, int32(1234), container.LivenessProbe.TimeoutSeconds)
 }
 
 func TestProbesOnKnativeService(t *testing.T) {
-	target := serving.Service{}
+	integration := &v1.Integration{
+		Spec: v1.IntegrationSpec{
+			Profile: v1.TraitProfileKnative,
+			Traits: map[string]v1.TraitSpec{
+				"knative-service": test.TraitSpecFromMap(t, map[string]interface{}{
+					"enabled": true,
+				}),
+				"container": test.TraitSpecFromMap(t, map[string]interface{}{
+					"probesEnabled":   true,
+					"expose":          true,
+					"livenessTimeout": 1234,
+				}),
+			},
+		},
+	}
 
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
+	env := newTestProbesEnv(t, integration)
 	env.Integration.Status.Phase = v1.IntegrationPhaseDeploying
-	env.Resources.Add(&target)
-
-	ctr := newTestContainerTrait()
-	ctr.Expose = BoolP(true)
-	ctr.LivenessTimeout = 1234
 
-	err := ctr.Apply(&env)
+	err := env.Catalog.apply(&env)
 	assert.Nil(t, err)
 
-	assert.Equal(t, "", target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Host)
-	assert.Equal(t, int32(0), target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Port.IntVal)
-	assert.Equal(t, defaultProbePath, target.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Path)
-	assert.Equal(t, "", target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Host)
-	assert.Equal(t, int32(0), target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Port.IntVal)
-	assert.Equal(t, defaultProbePath, target.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Path)
-	assert.Equal(t, int32(1234), target.Spec.Template.Spec.Containers[0].LivenessProbe.TimeoutSeconds)
-}
-
-func TestProbesOnKnativeServiceWithNoHttpPort(t *testing.T) {
-	target := serving.Service{}
+	container := env.GetIntegrationContainer()
 
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
-	env.Integration.Status.Phase = v1.IntegrationPhaseDeploying
-	env.Resources.Add(&target)
-
-	ctr := newTestContainerTrait()
-	ctr.PortName = "custom"
-	ctr.LivenessTimeout = 1234
-
-	err := ctr.Apply(&env)
-	assert.Nil(t, err)
-	assert.Nil(t, target.Spec.Template.Spec.Containers[0].LivenessProbe)
-	assert.Nil(t, target.Spec.Template.Spec.Containers[0].ReadinessProbe)
+	assert.Equal(t, "", container.LivenessProbe.HTTPGet.Host)
+	assert.Equal(t, int32(0), container.LivenessProbe.HTTPGet.Port.IntVal)
+	assert.Equal(t, defaultLivenessProbePath, container.LivenessProbe.HTTPGet.Path)
+	assert.Equal(t, "", container.ReadinessProbe.HTTPGet.Host)
+	assert.Equal(t, int32(0), container.ReadinessProbe.HTTPGet.Port.IntVal)
+	assert.Equal(t, defaultReadinessProbePath, container.ReadinessProbe.HTTPGet.Path)
+	assert.Equal(t, int32(1234), container.LivenessProbe.TimeoutSeconds)
 }
diff --git a/pkg/trait/container_test.go b/pkg/trait/container_test.go
index f36a0db..c4deae4 100644
--- a/pkg/trait/container_test.go
+++ b/pkg/trait/container_test.go
@@ -24,7 +24,6 @@ import (
 	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 
-	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -331,22 +330,37 @@ func TestContainerWithCustomImageAndDeprecatedIntegrationKit(t *testing.T) {
 }
 
 func TestContainerWithImagePullPolicy(t *testing.T) {
-	target := appsv1.Deployment{}
+	catalog, err := camel.DefaultCatalog()
+	assert.Nil(t, err)
 
-	env := newTestProbesEnv(t, v1.RuntimeProviderQuarkus)
-	env.Integration.Status.Phase = v1.IntegrationPhaseDeploying
-	env.Resources.Add(&target)
+	client, _ := test.NewFakeClient()
+	traitCatalog := NewCatalog(nil)
 
-	ctr := newTestContainerTrait()
-	ctr.ImagePullPolicy = "Always"
+	environment := Environment{
+		Ctx:          context.TODO(),
+		Client:       client,
+		CamelCatalog: catalog,
+		Catalog:      traitCatalog,
+		Integration: &v1.Integration{
+			Spec: v1.IntegrationSpec{
+				Profile: v1.TraitProfileKubernetes,
+				Traits: map[string]v1.TraitSpec{
+					"container": test.TraitSpecFromMap(t, map[string]interface{}{
+						"imagePullPolicy": "Always",
+					}),
+				},
+			},
+		},
+		Platform:  &v1.IntegrationPlatform{},
+		Resources: kubernetes.NewCollection(),
+	}
+	environment.Integration.Status.Phase = v1.IntegrationPhaseDeploying
+	environment.Platform.ResyncStatusFullConfig()
 
-	err := ctr.Apply(&env)
+	err = traitCatalog.apply(&environment)
 	assert.Nil(t, err)
-	assert.Equal(t, corev1.PullAlways, target.Spec.Template.Spec.Containers[0].ImagePullPolicy)
 
-	ctr.ImagePullPolicy = "MustFail"
+	container := environment.GetIntegrationContainer()
 
-	ok, err := ctr.Configure(&env)
-	assert.False(t, ok)
-	assert.NotNil(t, err)
+	assert.Equal(t, corev1.PullAlways, container.ImagePullPolicy)
 }
diff --git a/pkg/trait/health.go b/pkg/trait/health.go
new file mode 100644
index 0000000..0d60ca4
--- /dev/null
+++ b/pkg/trait/health.go
@@ -0,0 +1,195 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+	"encoding/json"
+	"sort"
+
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util"
+)
+
+const (
+	defaultLivenessProbePath  = "/q/health/live"
+	defaultReadinessProbePath = "/q/health/ready"
+)
+
+// The health trait is responsible for configuring the health probes on the integration container.
+//
+// It's disabled by default.
+//
+// +camel-k:trait=health
+type healthTrait struct {
+	BaseTrait `property:",squash"`
+
+	// Configures the liveness probe for the integration container (default `false`).
+	LivenessProbeEnabled *bool `property:"liveness-probe-enabled" json:"livenessProbeEnabled,omitempty"`
+	// Scheme to use when connecting to the liveness probe (default `HTTP`).
+	LivenessScheme string `property:"liveness-scheme" json:"livenessScheme,omitempty"`
+	// Number of seconds after the container has started before the liveness probe is initiated.
+	LivenessInitialDelay int32 `property:"liveness-initial-delay" json:"livenessInitialDelay,omitempty"`
+	// Number of seconds after which the liveness probe times out.
+	LivenessTimeout int32 `property:"liveness-timeout" json:"livenessTimeout,omitempty"`
+	// How often to perform the liveness probe.
+	LivenessPeriod int32 `property:"liveness-period" json:"livenessPeriod,omitempty"`
+	// Minimum consecutive successes for the liveness probe to be considered successful after having failed.
+	LivenessSuccessThreshold int32 `property:"liveness-success-threshold" json:"livenessSuccessThreshold,omitempty"`
+	// Minimum consecutive failures for the liveness probe to be considered failed after having succeeded.
+	LivenessFailureThreshold int32 `property:"liveness-failure-threshold" json:"livenessFailureThreshold,omitempty"`
+
+	// Configures the readiness probe for the integration container (default `true`).
+	ReadinessProbeEnabled *bool `property:"readiness-probe-enabled" json:"readinessProbeEnabled,omitempty"`
+	// Scheme to use when connecting to the readiness probe (default `HTTP`).
+	ReadinessScheme string `property:"readiness-scheme" json:"readinessScheme,omitempty"`
+	// Number of seconds after the container has started before the readiness probe is initiated.
+	ReadinessInitialDelay int32 `property:"readiness-initial-delay" json:"readinessInitialDelay,omitempty"`
+	// Number of seconds after which the readiness probe times out.
+	ReadinessTimeout int32 `property:"readiness-timeout" json:"readinessTimeout,omitempty"`
+	// How often to perform the readiness probe.
+	ReadinessPeriod int32 `property:"readiness-period" json:"readinessPeriod,omitempty"`
+	// Minimum consecutive successes for the readiness probe to be considered successful after having failed.
+	ReadinessSuccessThreshold int32 `property:"readiness-success-threshold" json:"readinessSuccessThreshold,omitempty"`
+	// Minimum consecutive failures for the readiness probe to be considered failed after having succeeded.
+	ReadinessFailureThreshold int32 `property:"readiness-failure-threshold" json:"readinessFailureThreshold,omitempty"`
+}
+
+func newHealthTrait() Trait {
+	return &healthTrait{
+		BaseTrait:       NewBaseTrait("health", 1700),
+		LivenessScheme:  string(corev1.URISchemeHTTP),
+		ReadinessScheme: string(corev1.URISchemeHTTP),
+	}
+}
+
+func (t *healthTrait) Configure(e *Environment) (bool, error) {
+	if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() {
+		return false, nil
+	}
+
+	if IsNilOrFalse(t.Enabled) {
+		// Source the configuration from the container trait to maintain backward compatibility.
+		// This can be removed once the deprecated properties related to health probes are actually
+		// removed from the container trait.
+		if trait := e.Catalog.GetTrait(containerTraitID); trait != nil {
+			if container, ok := trait.(*containerTrait); ok && IsNilOrTrue(container.Enabled) && IsTrue(container.DeprecatedProbesEnabled) {
+				config, err := json.Marshal(container)
+				if err != nil {
+					return false, err
+				}
+				err = json.Unmarshal(config, t)
+				if err != nil {
+					return false, err
+				}
+				t.Enabled = BoolP(true)
+				t.LivenessProbeEnabled = BoolP(true)
+				t.ReadinessProbeEnabled = BoolP(true)
+				return true, err
+			}
+		}
+		return false, nil
+	}
+
+	return true, nil
+}
+
+func (t *healthTrait) Apply(e *Environment) error {
+	if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
+		if capability, ok := e.CamelCatalog.Runtime.Capabilities[v1.CapabilityHealth]; ok {
+			for _, dependency := range capability.Dependencies {
+				util.StringSliceUniqueAdd(&e.Integration.Status.Dependencies, dependency.GetDependencyID())
+			}
+			// sort the dependencies to get always the same list if they don't change
+			sort.Strings(e.Integration.Status.Dependencies)
+		}
+		return nil
+	}
+
+	if IsNilOrFalse(t.LivenessProbeEnabled) && IsFalse(t.ReadinessProbeEnabled) {
+		return nil
+	}
+
+	container := e.GetIntegrationContainer()
+	var port *intstr.IntOrString
+	// Use the default named HTTP container port if it exists.
+	// For Knative, the Serving webhook is responsible for setting the user-land port,
+	// and associating the probes with the corresponding port.
+	if containerPort := e.getIntegrationContainerPort(); containerPort != nil && containerPort.Name == defaultContainerPortName {
+		p := intstr.FromString(defaultContainerPortName)
+		port = &p
+	} else if e.GetTrait(knativeServiceTraitID) == nil {
+		p := intstr.FromInt(defaultContainerPort)
+		port = &p
+	}
+
+	if IsTrue(t.LivenessProbeEnabled) {
+		container.LivenessProbe = t.newLivenessProbe(port, defaultLivenessProbePath)
+	}
+	if IsNilOrTrue(t.ReadinessProbeEnabled) {
+		container.ReadinessProbe = t.newReadinessProbe(port, defaultReadinessProbePath)
+	}
+
+	return nil
+}
+
+func (t *healthTrait) newLivenessProbe(port *intstr.IntOrString, path string) *corev1.Probe {
+	p := corev1.Probe{
+		Handler: corev1.Handler{
+			HTTPGet: &corev1.HTTPGetAction{
+				Path:   path,
+				Scheme: corev1.URIScheme(t.LivenessScheme),
+			},
+		},
+		InitialDelaySeconds: t.LivenessInitialDelay,
+		TimeoutSeconds:      t.LivenessTimeout,
+		PeriodSeconds:       t.LivenessPeriod,
+		SuccessThreshold:    t.LivenessSuccessThreshold,
+		FailureThreshold:    t.LivenessFailureThreshold,
+	}
+
+	if port != nil {
+		p.Handler.HTTPGet.Port = *port
+	}
+
+	return &p
+}
+
+func (t *healthTrait) newReadinessProbe(port *intstr.IntOrString, path string) *corev1.Probe {
+	p := corev1.Probe{
+		Handler: corev1.Handler{
+			HTTPGet: &corev1.HTTPGetAction{
+				Path:   path,
+				Scheme: corev1.URIScheme(t.ReadinessScheme),
+			},
+		},
+		InitialDelaySeconds: t.ReadinessInitialDelay,
+		TimeoutSeconds:      t.ReadinessTimeout,
+		PeriodSeconds:       t.ReadinessPeriod,
+		SuccessThreshold:    t.ReadinessSuccessThreshold,
+		FailureThreshold:    t.ReadinessFailureThreshold,
+	}
+
+	if port != nil {
+		p.Handler.HTTPGet.Port = *port
+	}
+
+	return &p
+}
diff --git a/pkg/trait/jolokia.go b/pkg/trait/jolokia.go
index 819f9de..b96a70d 100644
--- a/pkg/trait/jolokia.go
+++ b/pkg/trait/jolokia.go
@@ -99,7 +99,7 @@ func (t *jolokiaTrait) Apply(e *Environment) (err error) {
 		return nil
 	}
 
-	container := e.getIntegrationContainer()
+	container := e.GetIntegrationContainer()
 	if container == nil {
 		e.Integration.Status.SetCondition(
 			v1.IntegrationConditionJolokiaAvailable,
diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go
index 7810cc7..7d1991a 100644
--- a/pkg/trait/jvm.go
+++ b/pkg/trait/jvm.go
@@ -132,7 +132,7 @@ func (t *jvmTrait) Apply(e *Environment) error {
 		)
 	}
 
-	container := e.getIntegrationContainer()
+	container := e.GetIntegrationContainer()
 	if container == nil {
 		return nil
 	}
diff --git a/pkg/trait/jvm_test.go b/pkg/trait/jvm_test.go
index bc47aac..4360692 100644
--- a/pkg/trait/jvm_test.go
+++ b/pkg/trait/jvm_test.go
@@ -201,7 +201,7 @@ func TestApplyJvmTraitWithExternalKitType(t *testing.T) {
 	err := trait.Apply(environment)
 	assert.Nil(t, err)
 
-	container := environment.getIntegrationContainer()
+	container := environment.GetIntegrationContainer()
 
 	assert.Equal(t, 3, len(container.Args))
 	assert.Equal(t, "-cp", container.Args[0])
diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go
index 7859e08..2b01dd1 100644
--- a/pkg/trait/knative_service.go
+++ b/pkg/trait/knative_service.go
@@ -33,6 +33,8 @@ import (
 )
 
 const (
+	knativeServiceTraitID = "knative-service"
+
 	// Auto-scaling annotations
 	knativeServingClassAnnotation    = "autoscaling.knative.dev/class"
 	knativeServingMetricAnnotation   = "autoscaling.knative.dev/metric"
@@ -89,7 +91,7 @@ var _ ControllerStrategySelector = &knativeServiceTrait{}
 
 func newKnativeServiceTrait() Trait {
 	return &knativeServiceTrait{
-		BaseTrait: NewBaseTrait("knative-service", 1400),
+		BaseTrait: NewBaseTrait(knativeServiceTraitID, 1400),
 	}
 }
 
diff --git a/pkg/trait/prometheus.go b/pkg/trait/prometheus.go
index 6db5c51..9dae4c8 100644
--- a/pkg/trait/prometheus.go
+++ b/pkg/trait/prometheus.go
@@ -71,7 +71,7 @@ func (t *prometheusTrait) Apply(e *Environment) (err error) {
 		return nil
 	}
 
-	container := e.getIntegrationContainer()
+	container := e.GetIntegrationContainer()
 	if container == nil {
 		e.Integration.Status.SetCondition(
 			v1.IntegrationConditionPrometheusAvailable,
diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go
index 4881d7d..c1c37b6 100644
--- a/pkg/trait/quarkus.go
+++ b/pkg/trait/quarkus.go
@@ -235,7 +235,7 @@ func (t *quarkusTrait) Apply(e *Environment) error {
 
 	case v1.IntegrationKitPhaseReady:
 		if e.IntegrationInRunningPhases() && t.isNativeIntegration(e) {
-			container := e.getIntegrationContainer()
+			container := e.GetIntegrationContainer()
 			if container == nil {
 				return fmt.Errorf("unable to find integration container: %s", e.Integration.Name)
 			}
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index 64e8a39..68d2997 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -31,6 +31,7 @@ func init() {
 	AddToTraits(newEnvironmentTrait)
 	AddToTraits(newErrorHandlerTrait)
 	AddToTraits(newGarbageCollectorTrait)
+	AddToTraits(newHealthTrait)
 	AddToTraits(newIngressTrait)
 	AddToTraits(newIstioTrait)
 	AddToTraits(newJolokiaTrait)
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index b0b0241..d4993b9 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -747,7 +747,7 @@ func (e *Environment) collectConfigurations(configurationType string) []map[stri
 	return collectConfigurations(configurationType, e.Platform, e.IntegrationKit, e.Integration)
 }
 
-func (e *Environment) getIntegrationContainer() *corev1.Container {
+func (e *Environment) GetIntegrationContainer() *corev1.Container {
 	containerName := defaultContainerName
 	dt := e.Catalog.GetTrait(containerTraitID)
 	if dt != nil {
@@ -758,7 +758,7 @@ func (e *Environment) getIntegrationContainer() *corev1.Container {
 }
 
 func (e *Environment) getIntegrationContainerPort() *corev1.ContainerPort {
-	container := e.getIntegrationContainer()
+	container := e.GetIntegrationContainer()
 	if container == nil {
 		return nil
 	}
diff --git a/resources/traits.yaml b/resources/traits.yaml
index f314b60..47ffab4 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -134,53 +134,60 @@ traits:
     description: 'The pull policy: Always|Never|IfNotPresent'
   - name: probes-enabled
     type: bool
-    description: ProbesEnabled enable/disable probes on the container (default `false`)
+    description: 'DeprecatedProbesEnabled enable/disable probes on the container (default
+      `false`)Deprecated: replaced by the health trait.'
   - name: liveness-scheme
     type: string
-    description: Scheme to use when connecting. Defaults to HTTP. Applies to the liveness
-      probe.
+    description: 'Scheme to use when connecting. Defaults to HTTP. Applies to the
+      liveness probe.Deprecated: replaced by the health trait.'
   - name: liveness-initial-delay
     type: int32
-    description: Number of seconds after the container has started before liveness
-      probes are initiated.
+    description: 'Number of seconds after the container has started before liveness
+      probes are initiated.Deprecated: replaced by the health trait.'
   - name: liveness-timeout
     type: int32
-    description: Number of seconds after which the probe times out. Applies to the
-      liveness probe.
+    description: 'Number of seconds after which the probe times out. Applies to the
+      liveness probe.Deprecated: replaced by the health trait.'
   - name: liveness-period
     type: int32
-    description: How often to perform the probe. Applies to the liveness probe.
+    description: 'How often to perform the probe. Applies to the liveness probe.Deprecated:
+      replaced by the health trait.'
   - name: liveness-success-threshold
     type: int32
-    description: Minimum consecutive successes for the probe to be considered successful
-      after having failed.Applies to the liveness probe.
+    description: 'Minimum consecutive successes for the probe to be considered successful
+      after having failed.Applies to the liveness probe.Deprecated: replaced by the
+      health trait.'
   - name: liveness-failure-threshold
     type: int32
-    description: Minimum consecutive failures for the probe to be considered failed
-      after having succeeded.Applies to the liveness probe.
+    description: 'Minimum consecutive failures for the probe to be considered failed
+      after having succeeded.Applies to the liveness probe.Deprecated: replaced by
+      the health trait.'
   - name: readiness-scheme
     type: string
-    description: Scheme to use when connecting. Defaults to HTTP. Applies to the readiness
-      probe.
+    description: 'Scheme to use when connecting. Defaults to HTTP. Applies to the
+      readiness probe.Deprecated: replaced by the health trait.'
   - name: readiness-initial-delay
     type: int32
-    description: Number of seconds after the container has started before readiness
-      probes are initiated.
+    description: 'Number of seconds after the container has started before readiness
+      probes are initiated.Deprecated: replaced by the health trait.'
   - name: readiness-timeout
     type: int32
-    description: Number of seconds after which the probe times out. Applies to the
-      readiness probe.
+    description: 'Number of seconds after which the probe times out. Applies to the
+      readiness probe.Deprecated: replaced by the health trait.'
   - name: readiness-period
     type: int32
-    description: How often to perform the probe. Applies to the readiness probe.
+    description: 'How often to perform the probe. Applies to the readiness probe.Deprecated:
+      replaced by the health trait.'
   - name: readiness-success-threshold
     type: int32
-    description: Minimum consecutive successes for the probe to be considered successful
-      after having failed.Applies to the readiness probe.
+    description: 'Minimum consecutive successes for the probe to be considered successful
+      after having failed.Applies to the readiness probe.Deprecated: replaced by the
+      health trait.'
   - name: readiness-failure-threshold
     type: int32
-    description: Minimum consecutive failures for the probe to be considered failed
-      after having succeeded.Applies to the readiness probe.
+    description: 'Minimum consecutive failures for the probe to be considered failed
+      after having succeeded.Applies to the readiness probe.Deprecated: replaced by
+      the health trait.'
 - name: cron
   platform: false
   profiles:
@@ -345,6 +352,69 @@ traits:
     type: ./pkg/trait.discoveryCacheType
     description: Discovery client cache to be used, either `disabled`, `disk` or `memory`
       (default `memory`)
+- name: health
+  platform: false
+  profiles:
+  - Kubernetes
+  - Knative
+  - OpenShift
+  description: The health trait is responsible for configuring the health probes on
+    the integration container. It's disabled by default.
+  properties:
+  - name: enabled
+    type: bool
+    description: Can be used to enable or disable a trait. All traits share this common
+      property.
+  - name: liveness-probe-enabled
+    type: bool
+    description: Configures the liveness probe for the integration container (default
+      `false`).
+  - name: liveness-scheme
+    type: string
+    description: Scheme to use when connecting to the liveness probe (default `HTTP`).
+  - name: liveness-initial-delay
+    type: int32
+    description: Number of seconds after the container has started before the liveness
+      probe is initiated.
+  - name: liveness-timeout
+    type: int32
+    description: Number of seconds after which the liveness probe times out.
+  - name: liveness-period
+    type: int32
+    description: How often to perform the liveness probe.
+  - name: liveness-success-threshold
+    type: int32
+    description: Minimum consecutive successes for the liveness probe to be considered
+      successful after having failed.
+  - name: liveness-failure-threshold
+    type: int32
+    description: Minimum consecutive failures for the liveness probe to be considered
+      failed after having succeeded.
+  - name: readiness-probe-enabled
+    type: bool
+    description: Configures the readiness probe for the integration container (default
+      `true`).
+  - name: readiness-scheme
+    type: string
+    description: Scheme to use when connecting to the readiness probe (default `HTTP`).
+  - name: readiness-initial-delay
+    type: int32
+    description: Number of seconds after the container has started before the readiness
+      probe is initiated.
+  - name: readiness-timeout
+    type: int32
+    description: Number of seconds after which the readiness probe times out.
+  - name: readiness-period
+    type: int32
+    description: How often to perform the readiness probe.
+  - name: readiness-success-threshold
+    type: int32
+    description: Minimum consecutive successes for the readiness probe to be considered
+      successful after having failed.
+  - name: readiness-failure-threshold
+    type: int32
+    description: Minimum consecutive failures for the readiness probe to be considered
+      failed after having succeeded.
 - name: ingress
   platform: false
   profiles: