You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2020/10/19 12:06:25 UTC

[camel-k] branch master updated (e660f94 -> a272bb4)

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

nferraro pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git.


    from e660f94  fix: Kamelet example
     new de73f77  Add a kamel debug command
     new a272bb4  Add doc about kamel debug command

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../assets/images/debugging/remote-debugger.png    | Bin 0 -> 70273 bytes
 docs/modules/ROOT/nav-end.adoc                     |  15 +-
 docs/modules/ROOT/nav.adoc                         |  11 --
 docs/modules/ROOT/pages/cli/cli.adoc               |   4 +
 .../ROOT/pages/troubleshooting/debugging.adoc      |  50 ++++++
 .../known-issues.adoc}                             |   4 +-
 go.sum                                             |   1 +
 pkg/cmd/debug.go                                   | 158 ++++++++++++++++++
 pkg/cmd/root.go                                    |  14 +-
 pkg/trait/jvm.go                                   |  19 ++-
 pkg/util/kubernetes/collection.go                  |  13 ++
 pkg/util/kubernetes/log/util.go                    |   7 +-
 pkg/util/kubernetes/portforward.go                 | 176 +++++++++++++++++++++
 13 files changed, 448 insertions(+), 24 deletions(-)
 create mode 100644 docs/modules/ROOT/assets/images/debugging/remote-debugger.png
 create mode 100644 docs/modules/ROOT/pages/troubleshooting/debugging.adoc
 rename docs/modules/ROOT/pages/{troubleshooting.adoc => troubleshooting/known-issues.adoc} (96%)
 create mode 100644 pkg/cmd/debug.go
 create mode 100644 pkg/util/kubernetes/portforward.go


[camel-k] 01/02: Add a kamel debug command

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit de73f77a492f4e103460aec03b0da719ebe88c14
Author: nicolaferraro <ni...@gmail.com>
AuthorDate: Thu Oct 15 17:43:22 2020 +0200

    Add a kamel debug command
---
 go.sum                             |   1 +
 pkg/cmd/debug.go                   | 158 +++++++++++++++++++++++++++++++++
 pkg/cmd/root.go                    |  14 ++-
 pkg/trait/jvm.go                   |  19 ++--
 pkg/util/kubernetes/collection.go  |  13 +++
 pkg/util/kubernetes/log/util.go    |   7 +-
 pkg/util/kubernetes/portforward.go | 176 +++++++++++++++++++++++++++++++++++++
 7 files changed, 378 insertions(+), 10 deletions(-)

diff --git a/go.sum b/go.sum
index 20d9cc2..2a6ab78 100644
--- a/go.sum
+++ b/go.sum
@@ -357,6 +357,7 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
diff --git a/pkg/cmd/debug.go b/pkg/cmd/debug.go
new file mode 100644
index 0000000..effe8fb
--- /dev/null
+++ b/pkg/cmd/debug.go
@@ -0,0 +1,158 @@
+/*
+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 cmd
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"os/signal"
+	"syscall"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	camelv1 "github.com/apache/camel-k/pkg/client/camel/clientset/versioned/typed/camel/v1"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+	k8slog "github.com/apache/camel-k/pkg/util/kubernetes/log"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func newCmdDebug(rootCmdOptions *RootCmdOptions) (*cobra.Command, *debugCmdOptions) {
+	options := debugCmdOptions{
+		RootCmdOptions: rootCmdOptions,
+	}
+
+	cmd := cobra.Command{
+		Use:     "debug [integration name]",
+		Short:   "Debug an integration running on Kubernetes",
+		Long:    `Set an integration running on the Kubernetes cluster in debug mode and forward ports in order to connect a remote debugger running on the local host.`,
+		Args:    options.validateArgs,
+		PreRunE: decode(&options),
+		RunE:    options.run,
+	}
+
+	cmd.Flags().Bool("suspend", true, "Suspend the integration on startup, to let the debugger attach from the beginning")
+	cmd.Flags().Uint("port", 5005, "Local port to use for port-forwarding")
+	cmd.Flags().Uint("remote-port", 5005, "Remote port to use for port-forwarding")
+
+	// completion support
+	configureKnownCompletions(&cmd)
+
+	return &cmd, &options
+}
+
+type debugCmdOptions struct {
+	*RootCmdOptions `json:"-"`
+	Suspend         bool `mapstructure:"suspend" yaml:",omitempty"`
+	Port            uint `mapstructure:"port" yaml:",omitempty"`
+	RemotePort      uint `mapstructure:"remote-port" yaml:",omitempty"`
+}
+
+func (o *debugCmdOptions) validateArgs(_ *cobra.Command, args []string) error {
+	if len(args) < 1 {
+		return errors.New("run expects 1 argument, received 0")
+	}
+	return nil
+}
+
+func (o *debugCmdOptions) run(cmd *cobra.Command, args []string) error {
+	c, err := o.GetCamelCmdClient()
+	if err != nil {
+		return err
+	}
+
+	name := args[0]
+
+	it, err := c.Integrations(o.Namespace).Get(o.Context, name, metav1.GetOptions{})
+	if err != nil && k8serrors.IsNotFound(err) {
+		return fmt.Errorf("integration %q not found in namespace %q", name, o.Namespace)
+	} else if err != nil {
+		return err
+	}
+
+	fmt.Fprintf(cmd.OutOrStdout(), "Enabling debug mode on integration %q...\n", name)
+	it, err = o.toggleDebug(c, it, true)
+	if err != nil {
+		return err
+	}
+
+	cs := make(chan os.Signal)
+	signal.Notify(cs, os.Interrupt, syscall.SIGTERM)
+	go func() {
+		<-cs
+		if o.Context.Err() != nil {
+			// Context canceled
+			return
+		}
+		fmt.Printf("Disabling debug mode on integration %q\n", name)
+		it, err := c.Integrations(o.Namespace).Get(o.Context, name, metav1.GetOptions{})
+		_, err = o.toggleDebug(c, it, false)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		os.Exit(0)
+	}()
+
+	cmdClient, err := o.GetCmdClient()
+	if err != nil {
+		return err
+	}
+
+	selector := fmt.Sprintf("camel.apache.org/debug=true,camel.apache.org/integration=%s", name)
+
+	go func() {
+		err = k8slog.PrintUsingSelector(o.Context, cmdClient, o.Namespace, "integration", selector, cmd.OutOrStdout())
+		if err != nil {
+			fmt.Println(err)
+		}
+	}()
+
+	return kubernetes.PortForward(o.Context, cmdClient, o.Namespace, selector, o.Port, o.RemotePort, cmd.OutOrStdout(), cmd.ErrOrStderr())
+}
+
+func (o *debugCmdOptions) toggleDebug(c *camelv1.CamelV1Client, it *v1.Integration, active bool) (*v1.Integration, error) {
+	if it.Spec.Traits == nil {
+		it.Spec.Traits = make(map[string]v1.TraitSpec)
+	}
+	traitSpec := it.Spec.Traits["jvm"]
+	jvmConfig := make(map[string]interface{})
+	if len(traitSpec.Configuration.RawMessage) > 0 {
+		if err := json.Unmarshal(traitSpec.Configuration.RawMessage, &jvmConfig); err != nil {
+			return it, err
+		}
+	}
+	if active {
+		jvmConfig["debug"] = true
+		jvmConfig["debugSuspend"] = o.Suspend
+	} else {
+		delete(jvmConfig, "debug")
+		delete(jvmConfig, "debugSuspend")
+	}
+
+	jvmConfigBytes, err := json.Marshal(jvmConfig)
+	if err != nil {
+		return it, err
+	}
+	traitSpec.Configuration.RawMessage = jvmConfigBytes
+	it.Spec.Traits["jvm"] = traitSpec
+
+	return c.Integrations(it.Namespace).Update(o.Context, it, metav1.UpdateOptions{})
+}
diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go
index 35592bd..6be4a68 100644
--- a/pkg/cmd/root.go
+++ b/pkg/cmd/root.go
@@ -22,11 +22,11 @@ import (
 	"os"
 	"strings"
 
-	"github.com/spf13/viper"
-
 	"github.com/apache/camel-k/pkg/client"
+	camelv1 "github.com/apache/camel-k/pkg/client/camel/clientset/versioned/typed/camel/v1"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
 )
 
 const kamelCommandLongDescription = `Apache Camel K is a lightweight integration platform, born on Kubernetes, with serverless
@@ -136,6 +136,7 @@ func addKamelSubcommands(cmd *cobra.Command, options *RootCmdOptions) {
 	cmd.AddCommand(newCmdOperator())
 	cmd.AddCommand(cmdOnly(newCmdBuilder(options)))
 	cmd.AddCommand(cmdOnly(newCmdInit(options)))
+	cmd.AddCommand(cmdOnly(newCmdDebug(options)))
 }
 
 func addHelpSubCommands(cmd *cobra.Command, options *RootCmdOptions) error {
@@ -188,6 +189,15 @@ func (command *RootCmdOptions) GetCmdClient() (client.Client, error) {
 	return command._client, err
 }
 
+// GetCamelCmdClient returns a client to access the Camel resources
+func (command *RootCmdOptions) GetCamelCmdClient() (*camelv1.CamelV1Client, error) {
+	c, err := command.GetCmdClient()
+	if err != nil {
+		return nil, err
+	}
+	return camelv1.NewForConfig(c.GetConfig())
+}
+
 // NewCmdClient returns a new client that can be used from command line tools
 func (command *RootCmdOptions) NewCmdClient() (client.Client, error) {
 	return client.NewOutOfClusterClient(command.KubeConfig)
diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go
index 91dc8d7..83db9ff 100644
--- a/pkg/trait/jvm.go
+++ b/pkg/trait/jvm.go
@@ -22,18 +22,15 @@ import (
 	"sort"
 	"strings"
 
-	infp "gopkg.in/inf.v0"
-
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util"
 	"github.com/pkg/errors"
 	"github.com/scylladb/go-set/strset"
-
+	infp "gopkg.in/inf.v0"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
-
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
-
-	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
-	"github.com/apache/camel-k/pkg/util"
 )
 
 // The JVM trait is used to configure the JVM that runs the integration.
@@ -126,6 +123,14 @@ func (t *jvmTrait) Apply(e *Environment) error {
 		args = append(args,
 			fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s",
 				suspend, t.DebugAddress))
+
+		// Add label to mark the pods with debug enabled
+		e.Resources.VisitPodTemplateMeta(func(meta *metav1.ObjectMeta) {
+			if meta.Labels == nil {
+				meta.Labels = make(map[string]string)
+			}
+			meta.Labels["camel.apache.org/debug"] = "true"
+		})
 	}
 
 	hasHeapSizeOption := false
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index 8ede041..4643e2f 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -415,6 +415,19 @@ func (c *Collection) VisitPodSpec(visitor func(container *corev1.PodSpec)) {
 	})
 }
 
+// VisitPodTemplateMeta executes the visitor function on all PodTemplate metadata inside deployments or other resources
+func (c *Collection) VisitPodTemplateMeta(visitor func(meta *metav1.ObjectMeta)) {
+	c.VisitDeployment(func(d *appsv1.Deployment) {
+		visitor(&d.Spec.Template.ObjectMeta)
+	})
+	c.VisitKnativeConfigurationSpec(func(cs *serving.ConfigurationSpec) {
+		visitor(&cs.Template.ObjectMeta)
+	})
+	c.VisitCronJob(func(d *v1beta1.CronJob) {
+		visitor(&d.Spec.JobTemplate.Spec.Template.ObjectMeta)
+	})
+}
+
 // VisitKnativeConfigurationSpec executes the visitor function on all knative ConfigurationSpec inside serving Services
 func (c *Collection) VisitKnativeConfigurationSpec(visitor func(container *serving.ConfigurationSpec)) {
 	c.VisitKnativeService(func(s *serving.Service) {
diff --git a/pkg/util/kubernetes/log/util.go b/pkg/util/kubernetes/log/util.go
index 5539d13..7d67246 100644
--- a/pkg/util/kubernetes/log/util.go
+++ b/pkg/util/kubernetes/log/util.go
@@ -30,7 +30,12 @@ import (
 
 // Print prints integrations logs to the stdout
 func Print(ctx context.Context, client kubernetes.Interface, integration *v1.Integration, out io.Writer) error {
-	scraper := NewSelectorScraper(client, integration.Namespace, integration.Name, v1.IntegrationLabel+"="+integration.Name)
+	return PrintUsingSelector(ctx, client, integration.Namespace, integration.Name, v1.IntegrationLabel+"="+integration.Name, out)
+}
+
+// PrintUsingSelector prints pod logs using a selector
+func PrintUsingSelector(ctx context.Context, client kubernetes.Interface, namespace, defaultContainerName, selector string, out io.Writer) error {
+	scraper := NewSelectorScraper(client, namespace, defaultContainerName, selector)
 	reader := scraper.Start(ctx)
 
 	if _, err := io.Copy(out, ioutil.NopCloser(reader)); err != nil {
diff --git a/pkg/util/kubernetes/portforward.go b/pkg/util/kubernetes/portforward.go
new file mode 100644
index 0000000..7b5994b
--- /dev/null
+++ b/pkg/util/kubernetes/portforward.go
@@ -0,0 +1,176 @@
+/*
+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 kubernetes
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"io"
+
+	"github.com/apache/camel-k/pkg/client"
+	"github.com/apache/camel-k/pkg/util/log"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/watch"
+	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
+	restclient "k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/portforward"
+	"k8s.io/client-go/transport/spdy"
+	"net/http"
+)
+
+func PortForward(ctx context.Context, c client.Client, ns, labelSelector string, localPort, remotePort uint, stdOut, stdErr io.Writer) error {
+	list, err := c.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{
+		LabelSelector: labelSelector,
+	})
+	if err != nil {
+		return err
+	}
+
+	var forwardPod *corev1.Pod
+	var forwardCtx context.Context
+	var forwardCtxCancel context.CancelFunc
+
+	setupPortForward := func(pod *corev1.Pod) error {
+		if forwardPod == nil && podReady(pod) {
+			forwardPod = pod
+			forwardCtx, forwardCtxCancel = context.WithCancel(ctx)
+			if _, err := portFowardPod(forwardCtx, c.GetConfig(), ns, forwardPod.Name, localPort, remotePort, stdOut, stdErr); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+
+	if len(list.Items) > 0 {
+		if err := setupPortForward(&list.Items[0]); err != nil {
+			return err
+		}
+	}
+
+	watcher, err := c.CoreV1().Pods(ns).Watch(ctx, metav1.ListOptions{
+		LabelSelector:   labelSelector,
+		ResourceVersion: list.ResourceVersion,
+	})
+	if err != nil {
+		return err
+	}
+
+	events := watcher.ResultChan()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		case e, ok := <-events:
+			if !ok {
+				return nil
+			}
+
+			switch e.Type {
+			case watch.Added:
+				pod := e.Object.(*corev1.Pod)
+				if err := setupPortForward(pod); err != nil {
+					return err
+				}
+			case watch.Modified:
+				pod := e.Object.(*corev1.Pod)
+				if err := setupPortForward(pod); err != nil {
+					return err
+				}
+			case watch.Deleted:
+				if forwardPod != nil && e.Object != nil {
+					deletedPod := e.Object.(*corev1.Pod)
+					if deletedPod.Name == forwardPod.Name {
+						forwardCtxCancel()
+						forwardPod = nil
+						forwardCtx = nil
+						forwardCtxCancel = nil
+					}
+				}
+			}
+		}
+	}
+}
+
+func portFowardPod(ctx context.Context, config *restclient.Config, ns, pod string, localPort, remotePort uint, stdOut, stdErr io.Writer) (host string, err error) {
+	c, err := corev1client.NewForConfig(config)
+	if err != nil {
+		return "", err
+	}
+
+	url := c.RESTClient().Post().
+		Resource("pods").
+		Namespace(ns).
+		Name(pod).
+		SubResource("portforward").
+		URL()
+
+	transport, upgrader, err := spdy.RoundTripperFor(config)
+	if err != nil {
+		return "", err
+	}
+
+	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
+	stopChan := make(chan struct{})
+	readyChan := make(chan struct{})
+	forwarder, err := portforward.New(dialer, []string{fmt.Sprintf("%d:%d", localPort, remotePort)}, stopChan, readyChan, stdOut, stdErr)
+	if err != nil {
+		return "", err
+	}
+
+	go func() {
+		// Start the port forwarder
+		err = forwarder.ForwardPorts()
+		if err != nil {
+			log.Errorf(err, "error while forwarding ports")
+		}
+	}()
+
+	go func() {
+		// Stop the port forwarder when the context ends
+		select {
+		case <-ctx.Done():
+			close(stopChan)
+		}
+	}()
+
+	select {
+	case <-readyChan:
+		ports, err := forwarder.GetPorts()
+		if err != nil {
+			return "", err
+		}
+		if len(ports) != 1 {
+			return "", errors.New("wrong ports opened")
+		}
+		return fmt.Sprintf("localhost:%d", ports[0].Local), nil
+	case <-ctx.Done():
+		return "", errors.New("context closed")
+	}
+}
+
+func podReady(pod *corev1.Pod) bool {
+	for _, c := range pod.Status.Conditions {
+		if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
+			return true
+		}
+	}
+	return false
+}


[camel-k] 02/02: Add doc about kamel debug command

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a272bb42b242784394a06b3df41da8d0b269975e
Author: nicolaferraro <ni...@gmail.com>
AuthorDate: Thu Oct 15 23:09:13 2020 +0200

    Add doc about kamel debug command
---
 .../assets/images/debugging/remote-debugger.png    | Bin 0 -> 70273 bytes
 docs/modules/ROOT/nav-end.adoc                     |  15 ++++++-
 docs/modules/ROOT/nav.adoc                         |  11 -----
 docs/modules/ROOT/pages/cli/cli.adoc               |   4 ++
 .../ROOT/pages/troubleshooting/debugging.adoc      |  50 +++++++++++++++++++++
 .../known-issues.adoc}                             |   4 +-
 6 files changed, 70 insertions(+), 14 deletions(-)

diff --git a/docs/modules/ROOT/assets/images/debugging/remote-debugger.png b/docs/modules/ROOT/assets/images/debugging/remote-debugger.png
new file mode 100644
index 0000000..3accca3
Binary files /dev/null and b/docs/modules/ROOT/assets/images/debugging/remote-debugger.png differ
diff --git a/docs/modules/ROOT/nav-end.adoc b/docs/modules/ROOT/nav-end.adoc
index 1e8f12e..11d29c4 100644
--- a/docs/modules/ROOT/nav-end.adoc
+++ b/docs/modules/ROOT/nav-end.adoc
@@ -1,3 +1,16 @@
-* xref:troubleshooting.adoc[Troubleshooting]
+* Troubleshooting
+** xref:troubleshooting/debugging.adoc[Debugging]
+** xref:troubleshooting/known-issues.adoc[Known Issues]
+* xref:kamelets/kamelets.adoc[Kamelets]
+* xref:architecture/architecture.adoc[Architecture]
+** xref:architecture/operator.adoc[Operator]
+*** xref:architecture/cr/integration-platform.adoc[IntegrationPlatform]
+*** xref:architecture/cr/integration.adoc[Integration]
+*** xref:architecture/cr/integration-kit.adoc[IntegrationKit]
+*** xref:architecture/cr/build.adoc[Build]
+*** xref:architecture/cr/camel-catalog.adoc[CamelCatalog]
+** xref:architecture/runtime.adoc[Runtime]
+** xref:architecture/traits.adoc[Traits]
+** xref:architecture/sources.adoc[Sources]
 * xref:uninstalling.adoc[Uninstalling]
 * xref:developers.adoc[Contributing]
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 6953c36..ef1ff50 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -53,14 +53,3 @@
 ** xref:traits:service.adoc[Service]
 ** xref:traits:tracing.adoc[Tracing]
 // End of autogenerated code - DO NOT EDIT! (trait-nav)
-* xref:kamelets/kamelets.adoc[Kamelets]
-* xref:architecture/architecture.adoc[Architecture]
-** xref:architecture/operator.adoc[Operator]
-*** xref:architecture/cr/integration-platform.adoc[IntegrationPlatform]
-*** xref:architecture/cr/integration.adoc[Integration]
-*** xref:architecture/cr/integration-kit.adoc[IntegrationKit]
-*** xref:architecture/cr/build.adoc[Build]
-*** xref:architecture/cr/camel-catalog.adoc[CamelCatalog]
-** xref:architecture/runtime.adoc[Runtime]
-** xref:architecture/traits.adoc[Traits]
-** xref:architecture/sources.adoc[Sources]
diff --git a/docs/modules/ROOT/pages/cli/cli.adoc b/docs/modules/ROOT/pages/cli/cli.adoc
index b90044c..26d220b 100644
--- a/docs/modules/ROOT/pages/cli/cli.adoc
+++ b/docs/modules/ROOT/pages/cli/cli.adoc
@@ -28,6 +28,10 @@ Some of the most used commands are:
 |Run an integration on Kubernetes
 |`kamel run Routes.java`
 
+|debug
+|Debug a remote integration using a local debugger
+|`kamel debug myintegration`
+
 |get
 |Get integrations deployed on Kubernetes
 |`kamel get`
diff --git a/docs/modules/ROOT/pages/troubleshooting/debugging.adoc b/docs/modules/ROOT/pages/troubleshooting/debugging.adoc
new file mode 100644
index 0000000..be4bfeb
--- /dev/null
+++ b/docs/modules/ROOT/pages/troubleshooting/debugging.adoc
@@ -0,0 +1,50 @@
+= Debugging Camel K Integrations
+
+Sometimes integrations can fail or behave unexpectedly for unknown reasons and a developer needs to investigate the cause of such behavior.
+Attaching a debugger to an integration is a common way to start the investigation.
+
+Even if Camel K integrations run on a Kubernetes cluster, it's very easy to attach a debugger to the remote integration using the CLI.
+
+Suppose you just launched an integration using the following command:
+
+[source,shell]
+----
+kamel run examples/Sample.java
+----
+
+An integration named `sample` should be running in the cluster.
+You can use the `kamel debug` command to put it in "debug mode".
+
+[source,shell]
+----
+kamel debug sample
+----
+
+A possible output that you may find is the following:
+
+[source,shell]
+----
+$ kamel debug sample
+Enabling debug mode on integration "sample"...
+[1] Monitoring pod sample-8455c8b985-5cxvc
+[1] exec java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005 -cp ./resources:[..omitted..] io.quarkus.runner.GeneratedMain
+[1] Listening for transport dt_socket at address: 5005
+Forwarding from 127.0.0.1:5005 -> 5005
+Forwarding from [::1]:5005 -> 5005
+----
+
+As you can see in the logs, the CLI has configured the integration in debug mode (see option `-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005`)
+and started forwarding the local port 5005 on the workstation machine (local port can be changed using the `--port` option) to the container port 5005, where the JVM is waiting for a debugger.
+
+The JVM is suspended and waits for a debugger to attach by default: this behavior can be turned off using the `--suspend=false` option.
+
+Last thing to do is, with your IDE opened on the **integration file (if using Java, Groovy or Kotlin), the Apache Camel project or the Camel K Runtime project**,
+to start a remote debugger on `localhost:5005`.
+
+The following picture shows the configuration of a remote debugger in IntelliJ Idea.
+
+image::debugging/remote-debugger.png[Configuration of the remote debugger in IntelliJ Idea]
+
+Once you configure a debugger, you can **add breakpoints** to various part of the code, then connect the debugger to trigger the JVM startup.
+
+When the debugging session is finished, hitting *ctrl+c* on the terminal where the kamel CLI is running will restore the integration to its original status.
diff --git a/docs/modules/ROOT/pages/troubleshooting.adoc b/docs/modules/ROOT/pages/troubleshooting/known-issues.adoc
similarity index 96%
rename from docs/modules/ROOT/pages/troubleshooting.adoc
rename to docs/modules/ROOT/pages/troubleshooting/known-issues.adoc
index c360cfb..846ec5f 100644
--- a/docs/modules/ROOT/pages/troubleshooting.adoc
+++ b/docs/modules/ROOT/pages/troubleshooting/known-issues.adoc
@@ -1,5 +1,5 @@
-[[troubleshooting]]
-= Troubleshooting
+[[known-issues]]
+= Known Issues
 
 == `Error during unshare(CLONE_NEWUSER): Invalid argument`