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 2023/06/27 12:31:37 UTC

[camel-k] branch main updated: Ref #4513: Allow to remote debug the Operator (#4517)

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

nfilotto 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 b6310bd83 Ref #4513: Allow to remote debug the Operator (#4517)
b6310bd83 is described below

commit b6310bd83933dcbd3a714712bf53559810c3c0f6
Author: Nicolas Filotto <es...@users.noreply.github.com>
AuthorDate: Tue Jun 27 14:31:30 2023 +0200

    Ref #4513: Allow to remote debug the Operator (#4517)
    
    ## Motivation
    
    Since the code refactoring for Camel-K 2, it is much harder to debug the operator, especially on MacOS. The goal of this task is to provide a way to remote debug the operator in order to be closer to the target environment and to be local OS agnostic.
    
    ## Modifications:
    
    * Add a debug mode in the makefile to add the required flags at build time to make the program compatible with the remote debugging
    * Publish a specific image with delve installed in debug mode
    * Configure the pod in such a way that the operator is launched through delve
    * Add a doc describing how to use it
    * Add MacOS files to git ignore
---
 .gitignore                                         |  4 +
 build/Dockerfile                                   | 10 ++-
 .../ROOT/pages/contributing/remote-debugging.adoc  | 90 ++++++++++++++++++++++
 pkg/cmd/install.go                                 | 13 ++++
 pkg/install/operator.go                            | 23 ++++++
 script/Makefile                                    | 22 ++++--
 6 files changed, 155 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore
index 42d3fa167..767e219e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -176,3 +176,7 @@ config/**/*.gen.json
 # Fabric8 CRDs
 java/target
 pkg/resources/resources.go
+
+# MAC OS files
+.DS_Store
+
diff --git a/build/Dockerfile b/build/Dockerfile
index 4694eb062..a4efab6b6 100644
--- a/build/Dockerfile
+++ b/build/Dockerfile
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM eclipse-temurin:17-jdk
+FROM eclipse-temurin:17-jdk as base
 
 ARG MAVEN_DEFAULT_VERSION="3.8.6"
 ARG MAVEN_HOME="/usr/share/maven"
@@ -56,3 +56,11 @@ RUN mkdir -p /etc/maven/m2 \
 USER 1000
 
 ADD build/_output/bin/kamel /usr/local/bin/kamel
+
+FROM golang:1.18 as go
+
+RUN go install github.com/go-delve/delve/cmd/dlv@latest
+
+FROM base as debug
+
+COPY --from=go /go/bin/dlv /usr/local/bin/dlv
diff --git a/docs/modules/ROOT/pages/contributing/remote-debugging.adoc b/docs/modules/ROOT/pages/contributing/remote-debugging.adoc
new file mode 100644
index 000000000..a7b304914
--- /dev/null
+++ b/docs/modules/ROOT/pages/contributing/remote-debugging.adoc
@@ -0,0 +1,90 @@
+[[remote-debugging]]
+= Remote Debugging Camel-K
+
+In this article, we describe the steps needed to be able to remotely debug the Camel-K operator directly from the K8s cluster.
+By doing so, you are sure that the operator is executed in the same context as your target environment, which is not the case
+if the operator is launched on the local machine.
+
+[[publish-image]]
+== Publish the image
+
+The first thing to do is to build a specific docker image of the Camel-K operator for the debug mode, indeed the `kamel` program
+will then be built without compiler optimizations, and inlining but also the docker image will launch the operator through
+https://github.com/go-delve/delve[`delve`] to be able to remote debug it.
+
+[source,shell]
+----
+DEBUG_MODE=true make images
+----
+
+Once done, a docker image of type `docker.io/apache/camel-k-debug:2.0.0-SNAPSHOT` has been pushed into your local docker image registry.
+
+If you are using Minikube, before executing the previous command make sure to set up properly the environment
+variables in your terminal by executing the command `eval $(minikube -p minikube docker-env)`, in that case the image is
+directly pushed into the registry of Minikube, so you can skip the end of the section.
+
+For other clusters like for example `kind` where the registry is accessible locally from `localhost:5001`, simply tag the
+image to match with the new host and port with the next command:
+
+[source,shell]
+----
+docker tag docker.io/apache/camel-k-debug:2.0.0-SNAPSHOT localhost:5001/apache/camel-k-debug:2.0.0-SNAPSHOT
+----
+
+Then push the image to the target registry with the next command:
+[source,shell]
+----
+docker push localhost:5001/apache/camel-k-debug:2.0.0-SNAPSHOT
+----
+
+To ensure that the image has been pushed with success, let's query the registry https://docs.docker.com/registry/spec/api/#listing-repositories[using the API]
+[source,shell]
+----
+curl http://localhost:5001/v2/_catalog
+{"repositories":["apache/camel-k-debug"]}
+----
+
+[[install-operator]]
+== Install the operator
+
+Since the docker image is ready to be used, we can now install the operator with the debugging flags to make sure that
+the operator will be launched properly with the debug port open on its pod.
+
+First, let's create a namespace in which the operator will be installed, here the namespace is `test`.
+[source,shell]
+----
+kubectl create ns test
+namespace/test created
+----
+
+Then, install the operator with the image that we built before
+[source,shell]
+----
+./kamel install --olm=false --operator-image apache/camel-k-debug:2.0.0-SNAPSHOT --debugging -n test
+----
+It will install the operator using `apache/camel-k-debug:2.0.0-SNAPSHOT` as docker image and launch it in debug mode.
+
+[[port-forward]]
+== Open the port on the pod
+
+The operator is now waiting for a remote connection, but to make it possible, we need to make the debugging port
+accessible from outside the cluster thanks to the following `port-forward` command:
+
+[source,shell]
+----
+kubectl port-forward -n test $(kubectl get po -l app=camel-k -oname -n test) 4040:4040
+Forwarding from 127.0.0.1:4040 -> 4040
+Forwarding from [::1]:4040 -> 4040
+----
+This command port forwards the port `4040` of the pod to the local port `4040` which makes it accessible from `localhost`.
+Where `4040` is the default port of delve configured in the pod manifest, but it can be changed when installing the
+operator thanks to the flag `--debugging-port=4040`.
+
+[[configure-ide]]
+== Configure your IDE
+
+At this stage, you simply need to configure your favorite IDE to remote debug the operator using `localhost` as host and
+`4040` as port:
+
+* https://www.jetbrains.com/help/go/attach-to-running-go-processes-with-debugger.html#step-3-create-the-remote-run-debug-configuration-on-the-client-computer[Configure IDEA]
+* https://go.googlesource.com/vscode-go/+/HEAD/docs/debugging.md#remote-debugging[Configure VSCode]
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index 738d9a092..7c95ba1c2 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -140,6 +140,11 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *installCmdO
 	cmd.Flags().Bool("monitoring", false, "To enable or disable the operator monitoring")
 	cmd.Flags().Int("monitoring-port", 8080, "The port of the metrics endpoint")
 
+	// debugging
+	cmd.Flags().Bool("debugging", false, "To enable or disable the operator debugging")
+	cmd.Flags().Int("debugging-port", 4040, "The port of the debugger")
+	cmd.Flags().String("debugging-path", "/usr/local/bin/kamel", "The path to the kamel executable file")
+
 	// Operator settings
 	cmd.Flags().StringArray("toleration", nil, "Add a Toleration to the operator Pod")
 	cmd.Flags().StringArray("node-selector", nil, "Add a NodeSelector to the operator Pod")
@@ -196,6 +201,9 @@ type installCmdOptions struct {
 	MaxRunningBuilds            int32    `mapstructure:"max-running-pipelines"`
 	Monitoring                  bool     `mapstructure:"monitoring"`
 	MonitoringPort              int32    `mapstructure:"monitoring-port"`
+	Debugging                   bool     `mapstructure:"debugging"`
+	DebuggingPort               int32    `mapstructure:"debugging-port"`
+	DebuggingPath               string   `mapstructure:"debugging-path"`
 	TraitProfile                string   `mapstructure:"trait-profile"`
 	Tolerations                 []string `mapstructure:"tolerations"`
 	NodeSelectors               []string `mapstructure:"node-selectors"`
@@ -427,6 +435,11 @@ func (o *installCmdOptions) setupOperator(
 			Enabled: o.Monitoring,
 			Port:    o.MonitoringPort,
 		},
+		Debugging: install.OperatorDebuggingConfiguration{
+			Enabled: o.Debugging,
+			Port:    o.DebuggingPort,
+			Path:    o.DebuggingPath,
+		},
 		Tolerations:           o.Tolerations,
 		NodeSelectors:         o.NodeSelectors,
 		ResourcesRequirements: o.ResourcesRequirements,
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index c76666a33..89d09fb69 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -57,6 +57,7 @@ type OperatorConfiguration struct {
 	ClusterType           string
 	Health                OperatorHealthConfiguration
 	Monitoring            OperatorMonitoringConfiguration
+	Debugging             OperatorDebuggingConfiguration
 	Tolerations           []string
 	NodeSelectors         []string
 	ResourcesRequirements []string
@@ -68,6 +69,12 @@ type OperatorHealthConfiguration struct {
 	Port int32
 }
 
+type OperatorDebuggingConfiguration struct {
+	Enabled bool
+	Port    int32
+	Path    string
+}
+
 type OperatorMonitoringConfiguration struct {
 	Enabled bool
 	Port    int32
@@ -210,6 +217,22 @@ func OperatorOrCollect(ctx context.Context, cmd *cobra.Command, c client.Client,
 				FSGroup: &ugfid,
 			}
 		}
+		if cfg.Debugging.Enabled {
+			if d, ok := o.(*appsv1.Deployment); ok {
+				if d.Labels["camel.apache.org/component"] == "operator" {
+					d.Spec.Template.Spec.Containers[0].Command = []string{"dlv",
+						fmt.Sprintf("--listen=:%d", cfg.Debugging.Port), "--headless=true", "--api-version=2",
+						"exec", cfg.Debugging.Path, "--", "operator", "--leader-election=false"}
+					d.Spec.Template.Spec.Containers[0].Ports = append(d.Spec.Template.Spec.Containers[0].Ports, corev1.ContainerPort{
+						Name:          "delve",
+						ContainerPort: cfg.Debugging.Port,
+					})
+					// In debug mode, the Liveness probe must be removed otherwise K8s will consider the pod as dead
+					// while debugging
+					d.Spec.Template.Spec.Containers[0].LivenessProbe = nil
+				}
+			}
+		}
 
 		if cfg.Global {
 			if d, ok := o.(*appsv1.Deployment); ok {
diff --git a/script/Makefile b/script/Makefile
index 2f41df8dc..25745eab1 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -54,7 +54,7 @@ RELEASE_GIT_REMOTE := origin
 GIT_COMMIT := $(shell if [ -d .git ]; then git rev-list -1 HEAD; else echo "$(CUSTOM_VERSION)"; fi)
 LINT_GOGC := 10
 LINT_DEADLINE := 10m
-
+DEBUG_MODE ?= false
 
 # olm bundle vars
 MANAGER := config/manager
@@ -121,6 +121,10 @@ endif
 
 GOFLAGS = -ldflags "$(GOLDFLAGS)" -trimpath
 
+ifeq ($(DEBUG_MODE),true)
+GOFLAGS += -gcflags="all=-N -l"
+endif
+
 define LICENSE_HEADER
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
@@ -432,6 +436,16 @@ else
 endif
 	cp build/_output/bin/kamel-$(IMAGE_ARCH) build/_output/bin/kamel
 
+TARGET_STAGE := base
+ifeq ($(DEBUG_MODE),true)
+	TARGET_STAGE := debug
+	CUSTOM_IMAGE := $(CUSTOM_IMAGE)-debug
+endif
+
+DOCKER_TAG := $(CUSTOM_IMAGE):$(CUSTOM_VERSION)
+ifneq ($(IMAGE_ARCH), amd64)
+	DOCKER_TAG := $(DOCKER_TAG)-$(IMAGE_ARCH)
+endif
 
 images: build kamel-overlay maven-overlay bundle-kamelets
 ifneq (,$(findstring SNAPSHOT,$(RUNTIME_VERSION)))
@@ -439,11 +453,7 @@ ifneq (,$(findstring SNAPSHOT,$(RUNTIME_VERSION)))
 endif
 	@echo "####### Building Camel K operator arch $(IMAGE_ARCH) container image..."
 	mkdir -p build/_maven_output
-ifeq ($(IMAGE_ARCH), amd64)
-	docker build --platform=linux/$(IMAGE_ARCH) -t $(CUSTOM_IMAGE):$(CUSTOM_VERSION) -f build/Dockerfile .
-else
-	docker build --platform=linux/$(IMAGE_ARCH) -t $(CUSTOM_IMAGE):$(CUSTOM_VERSION)-$(IMAGE_ARCH) -f build/Dockerfile .
-endif
+	docker build --target $(TARGET_STAGE) --platform=linux/$(IMAGE_ARCH) -t $(DOCKER_TAG) -f build/Dockerfile .
 
 # Mainly used for internal CI purposes
 image-push: