<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:hashnode="https://hashnode.com/rss"><channel><title><![CDATA[kftray]]></title><description><![CDATA[kftray]]></description><link>https://blog.kftray.app</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1713136666013/6e127c45-cf4b-4b9f-abb0-08094965ccfb.png</url><title>kftray</title><link>https://blog.kftray.app</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Dec 2024 05:00:07 GMT</lastBuildDate><atom:link href="https://blog.kftray.app/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="next" href="https://blog.kftray.app/rss.xml?page=2"/><atom:link rel="previous" href="https://blog.kftray.app/rss.xml"/><item><title><![CDATA[Local Kubernetes Cluster with Automatic kubectl port-forward configurations using kftray]]></title><description><![CDATA[Learn how to dynamically create kubectl port forwarding configurations based on Kubernetes service annotations using Terraform and Kind.]]></description><link>https://blog.kftray.app/local-kubernetes-cluster-with-automatic-kubectl-port-forward-configurations</link><guid isPermaLink="true">https://blog.kftray.app/local-kubernetes-cluster-with-automatic-kubectl-port-forward-configurations</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[software development]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Henrique Cavarsan]]></dc:creator><pubDate>Tue, 01 Oct 2024 01:48:51 GMT</pubDate><content:encoded>&lt;![CDATA[&lt;p&gt;Hello. In this post, I&apos;ll show you how to set up a local Kubernetes cluster using Kind, Terraform, and Kftray. We&apos;ll keep all services inside the cluster, avoiding the need for ingress controllers or exposing services like NodePort or LoadBalancer. I&apos;ll also walk you through the Terraform code used in this setup.&lt;/p&gt;&lt;p&gt;Terraform code used in this Blog Post: https://github.com/hcavarsan/kftray-k8s-tf-example&lt;/p&gt;&lt;p&gt;Full Demo:&lt;img src=&quot;https://raw.githubusercontent.com/hcavarsan/kftray-blog/175ab7b009619f33dd5224f10e110bd5d55c0504/public/img/auto-import.gif&quot; alt=&quot;kftui&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;heading-why-keep-services-inside-the-cluster&quot;&gt;Why Keep Services Inside the Cluster?&lt;/h2&gt;&lt;p&gt;Exposing services externally can add complexity and potential security risks. For local development or secure environments, it&apos;s often better to keep everything internal. By using &lt;code&gt;kubectl port-forward&lt;/code&gt; and automating it with Kftray, we can access services running inside the cluster without exposing them to the outside world.&lt;/p&gt;&lt;h2 id=&quot;heading-tools-youll-need&quot;&gt;Tools You&apos;ll Need&lt;/h2&gt;&lt;p&gt;Before starting, make sure you have the following installed:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/get-started&quot;&gt;Docker&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.terraform.io/downloads.html&quot;&gt;Terraform (v1.9.5)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;Kftray&lt;/a&gt; (you can choose between the GUI or TUI version)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-cloning-the-repository&quot;&gt;Cloning the Repository&lt;/h2&gt;&lt;p&gt;First, clone the repository that contains the Terraform code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/hcavarsan/kftray-k8s-tf-example&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; kftray-k8s-tf-example/terraform&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;heading-understanding-the-terraform-code&quot;&gt;Understanding the Terraform Code&lt;/h2&gt;&lt;p&gt;The Terraform code in this repository automates the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Creates a Kind Kubernetes cluster.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Deploys Helm charts for Argo CD, Prometheus, Alertmanager, Grafana, and Jaeger.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Sets up service annotations for Kftray to auto-import port-forward configurations.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-project-structure&quot;&gt;Project Structure&lt;/h3&gt;&lt;p&gt;Here&apos;s how the project is organized:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;kftray-k8s-tf-example terraform    helm.tf    outputs.tf    locals.tf    providers.tf    variables.tf    templates       argocd-values.yaml.tpl       grafana-values.yaml.tpl       jaeger-values.yaml.tpl       prometheus-values.yaml.tpl    kind.tf Makefile docs    kftray.gif    kftui.gif README.md&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-providerstf&quot;&gt;providers.tf&lt;/h3&gt;&lt;p&gt;This file specifies the Terraform providers we&apos;ll use:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;terraform {  required_version = &quot;&amp;gt;= 1.0.0&quot;  required_providers {    kind = {      source  = &quot;tehcyx/kind&quot;      version = &quot;0.4.0&quot;    }    helm = {      source  = &quot;hashicorp/helm&quot;      version = &quot;&amp;gt;= 2.0.0&quot;    }    kubernetes = {      source  = &quot;hashicorp/kubernetes&quot;      version = &quot;&amp;gt;= 2.0.0&quot;    }    template = {      source  = &quot;hashicorp/template&quot;      version = &quot;&amp;gt;= 2.0.0&quot;    }  }}provider &quot;kind&quot; {}provider &quot;kubernetes&quot; {  config_path = kind_cluster.default.kubeconfig_path}provider &quot;helm&quot; {  kubernetes {    config_path = kind_cluster.default.kubeconfig_path  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kind&lt;/strong&gt;: Manages Kind clusters.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kubernetes&lt;/strong&gt;: Interacts with the Kubernetes cluster.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;helm&lt;/strong&gt;: Deploys Helm charts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;template&lt;/strong&gt;: Processes template files.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-variablestf&quot;&gt;variables.tf&lt;/h3&gt;&lt;p&gt;Defines variables used in the Terraform configuration:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;variable &quot;cluster_name&quot; {  description = &quot;Name of the Kind cluster&quot;  type        = string  default     = &quot;kftray-cluster&quot;}variable &quot;kubernetes_version&quot; {  description = &quot;Version of the Kind node image&quot;  type        = string  default     = &quot;v1.30.4&quot;}variable &quot;kubeconfig_dir&quot; {  description = &quot;Directory to store the kubeconfig file&quot;  type        = string  default     = &quot;~/.kube&quot;}# Chart versionsvariable &quot;argocd_chart_version&quot; {  description = &quot;Version of the Argo CD Helm chart&quot;  type        = string  default     = &quot;5.19.12&quot;}variable &quot;prometheus_chart_version&quot; {  description = &quot;The version of the Prometheus chart to deploy.&quot;  type        = string  default     = &quot;25.27.0&quot;}variable &quot;grafana_chart_version&quot; {  description = &quot;The version of the Grafana chart to deploy.&quot;  type        = string  default     = &quot;8.5.0&quot;}variable &quot;jaeger_chart_version&quot; {  description = &quot;The version of the Jaeger chart to deploy.&quot;  type        = string  default     = &quot;3.3.1&quot;}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-kindtf&quot;&gt;kind.tf&lt;/h3&gt;&lt;p&gt;Creates the Kind cluster:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;resource &quot;kind_cluster&quot; &quot;default&quot; {  name            = var.cluster_name  node_image      = &quot;kindest/node:${var.kubernetes_version}&quot;  kubeconfig_path = pathexpand(&quot;${var.kubeconfig_dir}/kind-config-${var.cluster_name}&quot;)  wait_for_ready  = true  kind_config {    kind        = &quot;Cluster&quot;    api_version = &quot;kind.x-k8s.io/v1alpha4&quot;    node {      role = &quot;control-plane&quot;      extra_port_mappings {        container_port = 80        host_port      = 80        protocol       = &quot;TCP&quot;      }    }    node {      role = &quot;worker&quot;    }  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: Cluster name.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;node_image&lt;/strong&gt;: Kubernetes version.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kubeconfig_path&lt;/strong&gt;: Where to store the kubeconfig file.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;wait_for_ready&lt;/strong&gt;: Wait until the cluster is ready.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kind_config&lt;/strong&gt;: Custom Kind configuration.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-localstf&quot;&gt;locals.tf&lt;/h3&gt;&lt;p&gt;Defines local variables and service configurations:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;locals {  services = {    argocd = {      namespace  = &quot;argocd&quot;      repository = &quot;https://argoproj.github.io/argo-helm&quot;      chart      = &quot;argo-cd&quot;      version    = var.argocd_chart_version      kftray = {        server = {          alias       = &quot;argocd&quot;          local_port  = &quot;16080&quot;          target_port = &quot;http&quot;        }      }    }    # ... other services ...  }  services_values = {    for service_name, service in local.services :    service_name =&amp;gt; templatefile(&quot;${path.module}/templates/${service_name}-values.yaml.tpl&quot;, {      kftray = service.kftray    })  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;services&lt;/strong&gt;: A map of services to deploy.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kftray&lt;/strong&gt;: Port-forward configurations for Kftray.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;services_values&lt;/strong&gt;: Processes Helm values templates for each service.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-helmtf&quot;&gt;helm.tf&lt;/h3&gt;&lt;p&gt;Deploys the services using Helm:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;resource &quot;helm_release&quot; &quot;services&quot; {  depends_on = [kind_cluster.default]  for_each         = local.services  name             = each.key  namespace        = each.value.namespace  create_namespace = true  repository       = each.value.repository  chart            = each.value.chart  version          = each.value.version  values = [    local.services_values[each.key]  ]}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;for_each&lt;/strong&gt;: Iterates over each service.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: Release name.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;namespace&lt;/strong&gt;: Kubernetes namespace.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;repository&lt;/strong&gt;: Helm chart repository.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;chart&lt;/strong&gt;: Helm chart name.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;version&lt;/strong&gt;: Chart version.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;values&lt;/strong&gt;: Custom values for the Helm chart.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-templates&quot;&gt;templates/&lt;/h3&gt;&lt;p&gt;Contains Helm values templates for each service (e.g., &lt;code&gt;argocd-values.yaml.tpl&lt;/code&gt;). These templates inject the Kftray annotations into the service definitions.&lt;/p&gt;&lt;h3 id=&quot;heading-outputstf&quot;&gt;outputs.tf&lt;/h3&gt;&lt;p&gt;Defines outputs for the Terraform run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;output &quot;endpoint&quot; {  description = &quot;API endpoint for the Kind cluster.&quot;  value       = kind_cluster.default.endpoint}output &quot;kubeconfig&quot; {  description = &quot;Kubeconfig file for the Kind cluster.&quot;  value       = kind_cluster.default.kubeconfig  sensitive   = true}output &quot;credentials&quot; {  description = &quot;Credentials for authenticating with the Kind cluster.&quot;  value = {    client_certificate     = kind_cluster.default.client_certificate    client_key             = kind_cluster.default.client_key    cluster_ca_certificate = kind_cluster.default.cluster_ca_certificate  }  sensitive = true}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;heading-applying-the-terraform-configuration&quot;&gt;Applying the Terraform Configuration&lt;/h2&gt;&lt;p&gt;To apply the Terraform configuration and set up the cluster, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;make apply&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Initialize Terraform.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Create the Kind cluster.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Deploy the Helm charts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set up the service annotations.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-installing-kftray&quot;&gt;Installing Kftray&lt;/h2&gt;&lt;p&gt;Go to the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;Kftray GitHub page&lt;/a&gt; and follow the installation instructions for your operating system.&lt;/p&gt;&lt;h2 id=&quot;heading-importing-port-forward-configurations-into-kftray&quot;&gt;Importing Port-Forward Configurations into Kftray&lt;/h2&gt;&lt;h3 id=&quot;heading-using-kftray-gui&quot;&gt;Using Kftray GUI&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open Kftray and click the tray icon to open the main window.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the menu icon at the bottom left corner.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select &quot;Auto Import.&quot;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &quot;Set kubeconfig&quot; and choose the kubeconfig file created by Terraform (usually &lt;code&gt;~/.kube/kind-config-kftray-cluster&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the context &lt;code&gt;kftray-cluster&lt;/code&gt; from the dropdown menu.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &quot;Import&quot; to load the port-forward settings.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/hcavarsan/kftray-k8s-tf-example/refs/heads/main/docs/kftray.gif&quot; alt=&quot;Kftray Banner&quot; /&gt;&lt;/p&gt;&lt;p&gt;After importing, you can start port forwarding by toggling the switch next to each service or by clicking &quot;Start All.&quot;&lt;/p&gt;&lt;h3 id=&quot;heading-using-kftui-terminal-interface&quot;&gt;Using Kftui (Terminal Interface)&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Set the &lt;code&gt;KUBECONFIG&lt;/code&gt; environment variable:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; KUBECONFIG=&lt;span class=&quot;hljs-string&quot;&gt;&quot;~/.kube/kind-config-kftray-cluster&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Start Kftui:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt; kftui&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;code&gt;Tab&lt;/code&gt; to access the top menu and select &quot;Auto Import.&quot;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;code&gt;Ctrl+A&lt;/code&gt; to select all configurations.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;code&gt;F&lt;/code&gt; to start all port forwards.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj66296vgfaalg960mkk.gif&quot; alt=&quot;kftui&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;heading-accessing-your-services-locally&quot;&gt;Accessing Your Services Locally&lt;/h2&gt;&lt;p&gt;With port forwarding set up, you can access your services on your local machine:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Argo CD&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:16080&quot;&gt;http://localhost:16080&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prometheus&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:19090&quot;&gt;http://localhost:19090&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alertmanager&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:19093&quot;&gt;http://localhost:19093&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Grafana&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:13080&quot;&gt;http://localhost:13080&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:15090&quot;&gt;http://localhost:15090&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-custom-kftray-settings&quot;&gt;Custom Kftray Settings&lt;/h2&gt;&lt;h3 id=&quot;heading-adjusting-kftray-port-forwarding-settings&quot;&gt;Adjusting Kftray Port Forwarding Settings&lt;/h3&gt;&lt;p&gt;To customize how Kftray forwards ports, edit the &lt;code&gt;locals.tf&lt;/code&gt; file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;locals {  services = {    argocd = {      kftray = {        server = {          alias       = &quot;argocd&quot;          local_port  = &quot;16080&quot;          target_port = &quot;http&quot;        }      }    }    # Other services...  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;alias&lt;/strong&gt;: The name displayed in Kftray.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;local_port&lt;/strong&gt;: The port on your machine to access the service.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;target_port&lt;/strong&gt;: The service&apos;s port or port name inside the cluster.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-cleaning-up&quot;&gt;Cleaning Up&lt;/h2&gt;&lt;p&gt;If you want to destroy the cluster and remove all resources, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;make destroy&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will tear down the cluster and delete all the resources created by Terraform.&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;By keeping all services inside the cluster and using Kftray for port forwarding, we create a simpler and more secure environment. This setup is useful for local development and situations where you want to avoid exposing services externally.&lt;/p&gt;&lt;p&gt;Feel free to explore and modify the Terraform code to suit your needs. If you have any questions or need help, feel free to reach out.&lt;/p&gt;&lt;p&gt;Thanks for reading. You can find more of my work or get in touch on &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;]]&gt;</content:encoded><hashnode:content>&lt;![CDATA[&lt;p&gt;Hello. In this post, I&apos;ll show you how to set up a local Kubernetes cluster using Kind, Terraform, and Kftray. We&apos;ll keep all services inside the cluster, avoiding the need for ingress controllers or exposing services like NodePort or LoadBalancer. I&apos;ll also walk you through the Terraform code used in this setup.&lt;/p&gt;&lt;p&gt;Terraform code used in this Blog Post: https://github.com/hcavarsan/kftray-k8s-tf-example&lt;/p&gt;&lt;p&gt;Full Demo:&lt;img src=&quot;https://raw.githubusercontent.com/hcavarsan/kftray-blog/175ab7b009619f33dd5224f10e110bd5d55c0504/public/img/auto-import.gif&quot; alt=&quot;kftui&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;heading-why-keep-services-inside-the-cluster&quot;&gt;Why Keep Services Inside the Cluster?&lt;/h2&gt;&lt;p&gt;Exposing services externally can add complexity and potential security risks. For local development or secure environments, it&apos;s often better to keep everything internal. By using &lt;code&gt;kubectl port-forward&lt;/code&gt; and automating it with Kftray, we can access services running inside the cluster without exposing them to the outside world.&lt;/p&gt;&lt;h2 id=&quot;heading-tools-youll-need&quot;&gt;Tools You&apos;ll Need&lt;/h2&gt;&lt;p&gt;Before starting, make sure you have the following installed:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/get-started&quot;&gt;Docker&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.terraform.io/downloads.html&quot;&gt;Terraform (v1.9.5)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;Kftray&lt;/a&gt; (you can choose between the GUI or TUI version)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-cloning-the-repository&quot;&gt;Cloning the Repository&lt;/h2&gt;&lt;p&gt;First, clone the repository that contains the Terraform code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/hcavarsan/kftray-k8s-tf-example&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; kftray-k8s-tf-example/terraform&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;heading-understanding-the-terraform-code&quot;&gt;Understanding the Terraform Code&lt;/h2&gt;&lt;p&gt;The Terraform code in this repository automates the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Creates a Kind Kubernetes cluster.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Deploys Helm charts for Argo CD, Prometheus, Alertmanager, Grafana, and Jaeger.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Sets up service annotations for Kftray to auto-import port-forward configurations.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-project-structure&quot;&gt;Project Structure&lt;/h3&gt;&lt;p&gt;Here&apos;s how the project is organized:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;kftray-k8s-tf-example terraform    helm.tf    outputs.tf    locals.tf    providers.tf    variables.tf    templates       argocd-values.yaml.tpl       grafana-values.yaml.tpl       jaeger-values.yaml.tpl       prometheus-values.yaml.tpl    kind.tf Makefile docs    kftray.gif    kftui.gif README.md&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-providerstf&quot;&gt;providers.tf&lt;/h3&gt;&lt;p&gt;This file specifies the Terraform providers we&apos;ll use:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;terraform {  required_version = &quot;&amp;gt;= 1.0.0&quot;  required_providers {    kind = {      source  = &quot;tehcyx/kind&quot;      version = &quot;0.4.0&quot;    }    helm = {      source  = &quot;hashicorp/helm&quot;      version = &quot;&amp;gt;= 2.0.0&quot;    }    kubernetes = {      source  = &quot;hashicorp/kubernetes&quot;      version = &quot;&amp;gt;= 2.0.0&quot;    }    template = {      source  = &quot;hashicorp/template&quot;      version = &quot;&amp;gt;= 2.0.0&quot;    }  }}provider &quot;kind&quot; {}provider &quot;kubernetes&quot; {  config_path = kind_cluster.default.kubeconfig_path}provider &quot;helm&quot; {  kubernetes {    config_path = kind_cluster.default.kubeconfig_path  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kind&lt;/strong&gt;: Manages Kind clusters.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kubernetes&lt;/strong&gt;: Interacts with the Kubernetes cluster.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;helm&lt;/strong&gt;: Deploys Helm charts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;template&lt;/strong&gt;: Processes template files.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-variablestf&quot;&gt;variables.tf&lt;/h3&gt;&lt;p&gt;Defines variables used in the Terraform configuration:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;variable &quot;cluster_name&quot; {  description = &quot;Name of the Kind cluster&quot;  type        = string  default     = &quot;kftray-cluster&quot;}variable &quot;kubernetes_version&quot; {  description = &quot;Version of the Kind node image&quot;  type        = string  default     = &quot;v1.30.4&quot;}variable &quot;kubeconfig_dir&quot; {  description = &quot;Directory to store the kubeconfig file&quot;  type        = string  default     = &quot;~/.kube&quot;}# Chart versionsvariable &quot;argocd_chart_version&quot; {  description = &quot;Version of the Argo CD Helm chart&quot;  type        = string  default     = &quot;5.19.12&quot;}variable &quot;prometheus_chart_version&quot; {  description = &quot;The version of the Prometheus chart to deploy.&quot;  type        = string  default     = &quot;25.27.0&quot;}variable &quot;grafana_chart_version&quot; {  description = &quot;The version of the Grafana chart to deploy.&quot;  type        = string  default     = &quot;8.5.0&quot;}variable &quot;jaeger_chart_version&quot; {  description = &quot;The version of the Jaeger chart to deploy.&quot;  type        = string  default     = &quot;3.3.1&quot;}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-kindtf&quot;&gt;kind.tf&lt;/h3&gt;&lt;p&gt;Creates the Kind cluster:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;resource &quot;kind_cluster&quot; &quot;default&quot; {  name            = var.cluster_name  node_image      = &quot;kindest/node:${var.kubernetes_version}&quot;  kubeconfig_path = pathexpand(&quot;${var.kubeconfig_dir}/kind-config-${var.cluster_name}&quot;)  wait_for_ready  = true  kind_config {    kind        = &quot;Cluster&quot;    api_version = &quot;kind.x-k8s.io/v1alpha4&quot;    node {      role = &quot;control-plane&quot;      extra_port_mappings {        container_port = 80        host_port      = 80        protocol       = &quot;TCP&quot;      }    }    node {      role = &quot;worker&quot;    }  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: Cluster name.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;node_image&lt;/strong&gt;: Kubernetes version.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kubeconfig_path&lt;/strong&gt;: Where to store the kubeconfig file.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;wait_for_ready&lt;/strong&gt;: Wait until the cluster is ready.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kind_config&lt;/strong&gt;: Custom Kind configuration.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-localstf&quot;&gt;locals.tf&lt;/h3&gt;&lt;p&gt;Defines local variables and service configurations:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;locals {  services = {    argocd = {      namespace  = &quot;argocd&quot;      repository = &quot;https://argoproj.github.io/argo-helm&quot;      chart      = &quot;argo-cd&quot;      version    = var.argocd_chart_version      kftray = {        server = {          alias       = &quot;argocd&quot;          local_port  = &quot;16080&quot;          target_port = &quot;http&quot;        }      }    }    # ... other services ...  }  services_values = {    for service_name, service in local.services :    service_name =&amp;gt; templatefile(&quot;${path.module}/templates/${service_name}-values.yaml.tpl&quot;, {      kftray = service.kftray    })  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;services&lt;/strong&gt;: A map of services to deploy.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;kftray&lt;/strong&gt;: Port-forward configurations for Kftray.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;services_values&lt;/strong&gt;: Processes Helm values templates for each service.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-helmtf&quot;&gt;helm.tf&lt;/h3&gt;&lt;p&gt;Deploys the services using Helm:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;resource &quot;helm_release&quot; &quot;services&quot; {  depends_on = [kind_cluster.default]  for_each         = local.services  name             = each.key  namespace        = each.value.namespace  create_namespace = true  repository       = each.value.repository  chart            = each.value.chart  version          = each.value.version  values = [    local.services_values[each.key]  ]}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;for_each&lt;/strong&gt;: Iterates over each service.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: Release name.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;namespace&lt;/strong&gt;: Kubernetes namespace.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;repository&lt;/strong&gt;: Helm chart repository.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;chart&lt;/strong&gt;: Helm chart name.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;version&lt;/strong&gt;: Chart version.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;values&lt;/strong&gt;: Custom values for the Helm chart.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;heading-templates&quot;&gt;templates/&lt;/h3&gt;&lt;p&gt;Contains Helm values templates for each service (e.g., &lt;code&gt;argocd-values.yaml.tpl&lt;/code&gt;). These templates inject the Kftray annotations into the service definitions.&lt;/p&gt;&lt;h3 id=&quot;heading-outputstf&quot;&gt;outputs.tf&lt;/h3&gt;&lt;p&gt;Defines outputs for the Terraform run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;output &quot;endpoint&quot; {  description = &quot;API endpoint for the Kind cluster.&quot;  value       = kind_cluster.default.endpoint}output &quot;kubeconfig&quot; {  description = &quot;Kubeconfig file for the Kind cluster.&quot;  value       = kind_cluster.default.kubeconfig  sensitive   = true}output &quot;credentials&quot; {  description = &quot;Credentials for authenticating with the Kind cluster.&quot;  value = {    client_certificate     = kind_cluster.default.client_certificate    client_key             = kind_cluster.default.client_key    cluster_ca_certificate = kind_cluster.default.cluster_ca_certificate  }  sensitive = true}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;heading-applying-the-terraform-configuration&quot;&gt;Applying the Terraform Configuration&lt;/h2&gt;&lt;p&gt;To apply the Terraform configuration and set up the cluster, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;make apply&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Initialize Terraform.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Create the Kind cluster.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Deploy the Helm charts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set up the service annotations.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-installing-kftray&quot;&gt;Installing Kftray&lt;/h2&gt;&lt;p&gt;Go to the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;Kftray GitHub page&lt;/a&gt; and follow the installation instructions for your operating system.&lt;/p&gt;&lt;h2 id=&quot;heading-importing-port-forward-configurations-into-kftray&quot;&gt;Importing Port-Forward Configurations into Kftray&lt;/h2&gt;&lt;h3 id=&quot;heading-using-kftray-gui&quot;&gt;Using Kftray GUI&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open Kftray and click the tray icon to open the main window.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the menu icon at the bottom left corner.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select &quot;Auto Import.&quot;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &quot;Set kubeconfig&quot; and choose the kubeconfig file created by Terraform (usually &lt;code&gt;~/.kube/kind-config-kftray-cluster&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the context &lt;code&gt;kftray-cluster&lt;/code&gt; from the dropdown menu.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &quot;Import&quot; to load the port-forward settings.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/hcavarsan/kftray-k8s-tf-example/refs/heads/main/docs/kftray.gif&quot; alt=&quot;Kftray Banner&quot; /&gt;&lt;/p&gt;&lt;p&gt;After importing, you can start port forwarding by toggling the switch next to each service or by clicking &quot;Start All.&quot;&lt;/p&gt;&lt;h3 id=&quot;heading-using-kftui-terminal-interface&quot;&gt;Using Kftui (Terminal Interface)&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Set the &lt;code&gt;KUBECONFIG&lt;/code&gt; environment variable:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; KUBECONFIG=&lt;span class=&quot;hljs-string&quot;&gt;&quot;~/.kube/kind-config-kftray-cluster&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Start Kftui:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt; kftui&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;code&gt;Tab&lt;/code&gt; to access the top menu and select &quot;Auto Import.&quot;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;code&gt;Ctrl+A&lt;/code&gt; to select all configurations.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;code&gt;F&lt;/code&gt; to start all port forwards.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj66296vgfaalg960mkk.gif&quot; alt=&quot;kftui&quot; /&gt;&lt;/p&gt;&lt;h2 id=&quot;heading-accessing-your-services-locally&quot;&gt;Accessing Your Services Locally&lt;/h2&gt;&lt;p&gt;With port forwarding set up, you can access your services on your local machine:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Argo CD&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:16080&quot;&gt;http://localhost:16080&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prometheus&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:19090&quot;&gt;http://localhost:19090&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alertmanager&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:19093&quot;&gt;http://localhost:19093&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Grafana&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:13080&quot;&gt;http://localhost:13080&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger&lt;/strong&gt;: &lt;a target=&quot;_blank&quot; href=&quot;http://localhost:15090&quot;&gt;http://localhost:15090&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-custom-kftray-settings&quot;&gt;Custom Kftray Settings&lt;/h2&gt;&lt;h3 id=&quot;heading-adjusting-kftray-port-forwarding-settings&quot;&gt;Adjusting Kftray Port Forwarding Settings&lt;/h3&gt;&lt;p&gt;To customize how Kftray forwards ports, edit the &lt;code&gt;locals.tf&lt;/code&gt; file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-plaintext&quot;&gt;locals {  services = {    argocd = {      kftray = {        server = {          alias       = &quot;argocd&quot;          local_port  = &quot;16080&quot;          target_port = &quot;http&quot;        }      }    }    # Other services...  }}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;alias&lt;/strong&gt;: The name displayed in Kftray.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;local_port&lt;/strong&gt;: The port on your machine to access the service.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;target_port&lt;/strong&gt;: The service&apos;s port or port name inside the cluster.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-cleaning-up&quot;&gt;Cleaning Up&lt;/h2&gt;&lt;p&gt;If you want to destroy the cluster and remove all resources, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;make destroy&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will tear down the cluster and delete all the resources created by Terraform.&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;By keeping all services inside the cluster and using Kftray for port forwarding, we create a simpler and more secure environment. This setup is useful for local development and situations where you want to avoid exposing services externally.&lt;/p&gt;&lt;p&gt;Feel free to explore and modify the Terraform code to suit your needs. If you have any questions or need help, feel free to reach out.&lt;/p&gt;&lt;p&gt;Thanks for reading. You can find more of my work or get in touch on &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;]]&gt;</hashnode:content><hashnode:coverImage>https://cdn.hashnode.com/res/hashnode/image/upload/v1727747211110/30b8b4ff-38f5-4f56-a3e6-ef5df4766c7f.png</hashnode:coverImage></item><item><title><![CDATA[Port Forward Transition from SPDY to WebSockets in Kubernetes 1.30]]></title><description><![CDATA[Kubernetes 1.30 shifts from SPDY to WebSockets, boosting efficiency and cluster management, with enhanced performance and compatibility]]></description><link>https://blog.kftray.app/port-forward-transition-from-spdy-to-websockets-in-kubernetes-130</link><guid isPermaLink="true">https://blog.kftray.app/port-forward-transition-from-spdy-to-websockets-in-kubernetes-130</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Devops]]></category><category><![CDATA[software development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[Henrique Cavarsan]]></dc:creator><pubDate>Mon, 29 Apr 2024 21:37:54 GMT</pubDate><content:encoded>&lt;![CDATA[&lt;h2 id=&quot;heading-introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;One of the changes in the latest Kubernetes version, 1.30 (Uwubnetes) is the transition from SPDY to websocket for some of the &lt;code&gt;kubectl&lt;/code&gt; commands. Most of the updates get most of the attention, but this is no less important.&lt;/p&gt;&lt;h2 id=&quot;heading-understanding-spdy&quot;&gt;Understanding SPDY:&lt;/h2&gt;&lt;p&gt;Google created SPDY to improve HTTP by speeding up webpage load times and enhancing security with a session layer. It allowed multiple data streams to be sent simultaneously, prioritized request values, and optimized communication by compressing headers. However, SPDY was deprecated in 2016 and replaced by HTTP/2, which is more advanced. Therefore, for technologies like Kubernetes that require better and standards communication methods, SPDY is no longer relevant.&lt;/p&gt;&lt;h2 id=&quot;heading-why-websockets&quot;&gt;Why WebSockets?&lt;/h2&gt;&lt;p&gt;WebSockets use a bidirectional communication protocol over a single TCP connection, widely supported in web servers, proxies, and network gateways. This increases compatibility and communication performance for &lt;code&gt;kubectl&lt;/code&gt; commands. Here are some points about WebSockets:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;WebSockets allow for full-duplex communication, so it&apos;s very efficient to send and receive data. It requires no multiple connections, that would require handshakes.&lt;/li&gt;&lt;li&gt;This technology reduces overhead and allows for high-speed data exchanges, which is especially interesting for Kubernetes-related tasks.&lt;/li&gt;&lt;li&gt;WebSockets are protocol-independent so they may be used for communication over any full-duplex single-connection protocol, which will reduce latency and help in real-time command execution.&lt;/li&gt;&lt;li&gt;WebSockets keep a persistent connection and thus use resources more efficiently and cut the number of TCP connections used, in turn reducing the amount of network overhead.&lt;/li&gt;&lt;li&gt;The use of WebSockets fix issues that older protocols like SPDY had, and it&apos;s more compatible with modern web technologies.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The migration from SPDY to WebSockets in Kubernetes makes it much easier to communicate control messages, which will allow for better cluster management and stability.&lt;/p&gt;&lt;h2 id=&quot;heading-kep-4006&quot;&gt;Kep 4006&lt;/h2&gt;&lt;p&gt;The SPDY protocol was deprecated in 2016. In 2023, a proposal was opened in the Kubernetes community to change the protocol for commands like port-forward, attach, exec, and portforward that use upgraded connections.&lt;/p&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://github.com/kubernetes/enhancements/issues/4006&quot;&gt;Link to proposal&lt;/a&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://opengraph.githubassets.com/1/kubernetes/enhancements/issues/4006&quot;&gt;Link to issue&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;heading-implementation-overview&quot;&gt;Implementation Overview:&lt;/h3&gt;&lt;p&gt;WebSocket support in Kubernetes and kubectl starts with an HTTP handshake that upgrades the connection to a WebSocket, like as the SPDY. The WebSocket framing method provides improved technique for data framing for streams such as stdin, stdout, and stderr through framing with a channel identifier. This is an extension of the technique once used by SPDY through binary data framing.&lt;/p&gt;&lt;p&gt;As a fallback mechanism in case WebSocket support is not available on the server, a fallback to SPDY is utilized. The initial performance assessments demonstrated that WebSocket is as efficient as, or even a little better than, SPDY for standard kubectl operations, and continuing performance testing is intended to better performance across multiple use cases. There are many implications from this transition, including greater network compatibility in settings where strong proxy and firewall policies are in place, and better integration with external tools.&lt;/p&gt;&lt;h3 id=&quot;heading-api-server-changes&quot;&gt;API server changes:&lt;/h3&gt;&lt;p&gt;The API server now supports the WebSocket protocol by replacing SPDY with explicit headers to enable bidirectional streams. Additionally, the &lt;code&gt;StreamTranslatorProxy&lt;/code&gt; has been added to help create WebSocket connections from clients to internal SPDY connections in Kubernetes.&lt;/p&gt;&lt;p&gt;The management of WebSocket frames is critical, especially for the occasional use of the ping/pong frames to keep the WebSocket connection alive. This feature was first introduced in an alpha version that one could use through the feature gate for port forwarding Websockets within the API server and a corresponding flag in &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&quot;heading-client-kubectl-changes&quot;&gt;Client (&lt;code&gt;kubectl&lt;/code&gt;) Changes:&lt;/h3&gt;&lt;p&gt;On the client side, it added an upgrade to WebSocket for port forwarding, along with signing, response processing, and  data serialization and deserialization over the WebSocket. This gives the user the ability to enable WebSocket port forwarding  through environment variables. To make this transition easy, kubectl has also introduced mechanisms to handle errors and reliability, with error handling during its alpha and beta phases, along with a fallback to SPDY in case the WebSocket connection fails to ensure that service continuity is not lost.&lt;/p&gt;&lt;h2 id=&quot;heading-websockets-in-kubectl-port-forward-with-kind&quot;&gt;Websockets in &lt;code&gt;kubectl port-forward&lt;/code&gt; with Kind&lt;/h2&gt;&lt;h3 id=&quot;heading-setup-env&quot;&gt;Setup Env&lt;/h3&gt;&lt;p&gt;Before creating my environment, Docker, Kind, and Go have to be installed on the machine. All of these are needed for developing and deploying our Kubernetes cluster on our local machines. Let&apos;s set up the environment with Kubernetes 1.30.&lt;/p&gt;&lt;p&gt;Let&apos;s start by setting up the necessary environment variables and dependencies:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Set Go environment variables&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; GOPATH=&lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/go&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; GOROOT=/usr/&lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt;/go&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; PATH=&lt;span class=&quot;hljs-variable&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;hljs-variable&quot;&gt;$GOROOT&lt;/span&gt;/bin:&lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/bin&lt;span class=&quot;hljs-comment&quot;&gt;# Create a directory for Kubernetes source&lt;/span&gt;mkdir -p &lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/src/k8s.io&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/src/k8s.io&lt;span class=&quot;hljs-comment&quot;&gt;# Clone the Kubernetes repository&lt;/span&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/kubernetes/kubernetes.git&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; kubernetes&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;heading-install-dependencies-on-mac-m1&quot;&gt;Install Dependencies on Mac M1&lt;/h4&gt;&lt;p&gt;For those uses Mac with an M1 chip:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;brew install bashbrew install gnu-tar&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-building-the-kubernetes-and-kubectl-130-image&quot;&gt;Building the kubernetes and kubectl 1.30 image&lt;/h3&gt;&lt;p&gt;To build the images and kubectl, we need to have Go 1.22 or later. Then, execute some commands:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Checkout to the Kubernetes 1.30 release&lt;/span&gt;git checkout release-1.30&lt;span class=&quot;hljs-comment&quot;&gt;# Build a node image using Kind&lt;/span&gt;kind build node-image&lt;span class=&quot;hljs-comment&quot;&gt;# Build the kubectl in this version&lt;/span&gt;make kubectl KUBE_BUILD_PLATFORMS=darwin/arm64&lt;span class=&quot;hljs-comment&quot;&gt;# kubectl artifact output path : $GOPATH/kubernetes/_output/dockerized/bin/darwin/arm64/kubectl&lt;/span&gt;chmod +x &lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/kubernetes/_output/dockerized/bin/darwin/arm64/kubectl&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-kind-cluster-configs&quot;&gt;Kind Cluster Configs&lt;/h3&gt;&lt;p&gt;First, set up the kind cluster with 1.30 images by creating a &lt;code&gt;config.yaml&lt;/code&gt; file with featureGates &lt;code&gt;PortForwardWebsockets&lt;/code&gt; enabled:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Cluster&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;featureGates:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;PortForwardWebsockets&quot;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;nodes:&lt;/span&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;role:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;control-plane&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kindest/node:latest&lt;/span&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;role:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;worker&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kindest/node:latest&lt;/span&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;role:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;worker&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kindest/node:latest&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-create-the-cluster&quot;&gt;Create the cluster&lt;/h3&gt;&lt;p&gt;When you have your configuration file set up you can add the cluster with the following command:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Change default kubeconfig path to have only with kind test cluster&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; KUBECONFIG=~/.kube/kind-kindkind create cluster --name kind-1-30 --config config.yaml&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-deploy-a-sample-app&quot;&gt;Deploy a sample app&lt;/h3&gt;&lt;p&gt;You can test your new WebSocket capability by deploying a simple application and using the cluster to connect by port forwarding:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/digitalocean/kubernetes-sample-apps.git&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; kubernetes-sample-appskubectl apply -k bookinfo-example/kustomize&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-forward-the-port-using-websockets&quot;&gt;Forward the Port Using WebSockets&lt;/h3&gt;&lt;p&gt;Enable the WebSocket port forwarding :&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; KUBECTL_PORT_FORWARD_WEBSOCKETS=&lt;span class=&quot;hljs-string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/kubernetes/_output/dockerized/bin/darwin/arm64/kubectl -v6 port-forward svc/productpage -n bookinfo 9080:9080&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, open &lt;code&gt;http://localhost:9080/&lt;/code&gt; in your browser. You should get responses from the echoserver.&lt;/p&gt;&lt;h3 id=&quot;heading-verifying-websocket-port-forwarding&quot;&gt;Verifying WebSocket Port Forwarding&lt;/h3&gt;&lt;p&gt;To confirm if WebSocket port forwarding is active, review the kubectl logs:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;This is the log with SDPY (default):&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.963059&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;466&lt;/span&gt;] curl -v -XPOST  -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;X-Stream-Protocol-Version: portforward.k8s.io&quot;&lt;/span&gt; -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;User-Agent: kubectl/v1.31.0 (darwin/arm64) kubernetes/cae35db&quot;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&apos;https://192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward&apos;&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.965663&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;510&lt;/span&gt;] HTTP Trace: Dial to tcp:&lt;span class=&quot;hljs-number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.68&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.109&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;6443&lt;/span&gt; succeedI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978422&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;553&lt;/span&gt;] POST https:&lt;span class=&quot;hljs-comment&quot;&gt;//192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward 101 Switching Protocols in 15 milliseconds&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978440&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;570&lt;/span&gt;] HTTP Statistics: DNSLookup &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ms Dial &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt; ms TLSHandshake &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ms Duration &lt;span class=&quot;hljs-number&quot;&gt;15&lt;/span&gt; msI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978444&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;577&lt;/span&gt;] Response Headers:I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978448&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Upgrade: SPDY/&lt;span class=&quot;hljs-number&quot;&gt;3.1&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978450&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     X-Stream-Protocol-Version: portforward.k8s.ioI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978453&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Connection: Upgrade&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;This is the logs with Websocket enabled:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.504179&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; tunneling_dialer.go:&lt;span class=&quot;hljs-number&quot;&gt;75&lt;/span&gt;] Before WebSocket Upgrade Connection...I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.504194&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;466&lt;/span&gt;] curl -v -XGET  -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;Sec-Websocket-Protocol: SPDY/3.1+portforward.k8s.io&quot;&lt;/span&gt; -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;User-Agent: kubectl/v1.31.0 (darwin/arm64) kubernetes/cae35db&quot;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&apos;https://192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward&apos;&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.506351&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;510&lt;/span&gt;] HTTP Trace: Dial to tcp:&lt;span class=&quot;hljs-number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.68&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.109&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;6443&lt;/span&gt; succeedI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519936&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;553&lt;/span&gt;] GET https:&lt;span class=&quot;hljs-comment&quot;&gt;//192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward 101 Switching Protocols in 15 milliseconds&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519954&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;570&lt;/span&gt;] HTTP Statistics: DNSLookup &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ms Dial &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt; ms TLSHandshake &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt; ms ServerProcessing &lt;span class=&quot;hljs-number&quot;&gt;8&lt;/span&gt; ms Duration &lt;span class=&quot;hljs-number&quot;&gt;15&lt;/span&gt; msI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519958&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;577&lt;/span&gt;] Response Headers:I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519962&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Sec-Websocket-Accept: +AqmlgtoGPP/Rlfw6oAZMCN34SY=I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519964&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Sec-Websocket-Protocol: SPDY/&lt;span class=&quot;hljs-number&quot;&gt;3.1&lt;/span&gt;+portforward.k8s.ioI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519966&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Upgrade: websocketI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519968&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Connection: UpgradeI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519973&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; tunneling_dialer.go:&lt;span class=&quot;hljs-number&quot;&gt;85&lt;/span&gt;] negotiated protocol: portforward.k8s.io&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The logs show that the protocol upgrade to SPDY via WebSocket was successful in Kubernetes 1.30&lt;/p&gt;&lt;h2 id=&quot;heading-kep-roadmap-port-forward-over-websockets&quot;&gt;KEP Roadmap Port Forward over Websockets&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Alpha Release (Kubernetes 1.30):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;WebSocket support added. Feature enabled via the &lt;code&gt;KUBECTL_PORT_FORWARD_WEBSOCKETS=true&lt;/code&gt; env var on the client side and  &lt;code&gt;PortForwardWebsockets&lt;/code&gt; alpha feature flag should be enabled on the API Server,.&lt;/p&gt;&lt;p&gt;Focus in the alpha phase would be testing from all perspectives, user feedback, and updating developer and user documentation for this alpha feature.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Beta Release(Target 1.31):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;More extensive testing for performance fine-tuning, user feedback, and updated documentation, the documentation will be updated regularly with the changes during the beta phase improvements.&lt;/p&gt;&lt;p&gt;User community feedback will be the basis in making the feature stable and usable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Stable Release (Target 1.32):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Ensure WebSocket support transitions from beta to stable with alpha feature flags removed and publish a deprecation notice to officially mark the transition away from SPDY-based port forwarding.&lt;/p&gt;&lt;h2 id=&quot;heading-kep-roadmap-remotecommand-over-websockets-exec-cp-attach&quot;&gt;KEP Roadmap RemoteCommand over websockets (exec, cp, attach)&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Beta release (Kubernetes 1.30):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;WebSocket for &lt;code&gt;RemoteCommand&lt;/code&gt; (exec, cp, attach) will be on by default. The feature gates to the client and server are expected to ensure that the SPDY replacement has as little impact on the user as possible.&lt;/p&gt;&lt;p&gt;The biggest emphasis here is on making the changes transparent to the user and, for those who are wary of this new implementation, will always have the choice to disable it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Path to Stability:&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Post 1.30 and leading up to the 1.32 release, the focus will be on refinement in WebSocket functionality, all aimed at reaching zero known critical bugs.&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;And thats all, folks. This hopefully explains the important protocol communication change in Kubernetes. I&apos;ve had too many issues due to SPDY and lack of compatibility with new load balancers, proxies, etc., which was so complicated that I found myself at work. Now I see this migration from SPDY to WebSockets, so I&apos;m a bit excited.&lt;/p&gt;&lt;p&gt;Looking forward, I&apos;ll let you know if there are any updates about this KEP&lt;/p&gt;&lt;p&gt;Also, maybe there&apos;ll be errors or misinformation in this post; I&apos;m just human after all. Feel free to let me know if you find anything off on social media or post a comment here at the blog. Thanks!&lt;/p&gt;]]&gt;</content:encoded><hashnode:content>&lt;![CDATA[&lt;h2 id=&quot;heading-introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;One of the changes in the latest Kubernetes version, 1.30 (Uwubnetes) is the transition from SPDY to websocket for some of the &lt;code&gt;kubectl&lt;/code&gt; commands. Most of the updates get most of the attention, but this is no less important.&lt;/p&gt;&lt;h2 id=&quot;heading-understanding-spdy&quot;&gt;Understanding SPDY:&lt;/h2&gt;&lt;p&gt;Google created SPDY to improve HTTP by speeding up webpage load times and enhancing security with a session layer. It allowed multiple data streams to be sent simultaneously, prioritized request values, and optimized communication by compressing headers. However, SPDY was deprecated in 2016 and replaced by HTTP/2, which is more advanced. Therefore, for technologies like Kubernetes that require better and standards communication methods, SPDY is no longer relevant.&lt;/p&gt;&lt;h2 id=&quot;heading-why-websockets&quot;&gt;Why WebSockets?&lt;/h2&gt;&lt;p&gt;WebSockets use a bidirectional communication protocol over a single TCP connection, widely supported in web servers, proxies, and network gateways. This increases compatibility and communication performance for &lt;code&gt;kubectl&lt;/code&gt; commands. Here are some points about WebSockets:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;WebSockets allow for full-duplex communication, so it&apos;s very efficient to send and receive data. It requires no multiple connections, that would require handshakes.&lt;/li&gt;&lt;li&gt;This technology reduces overhead and allows for high-speed data exchanges, which is especially interesting for Kubernetes-related tasks.&lt;/li&gt;&lt;li&gt;WebSockets are protocol-independent so they may be used for communication over any full-duplex single-connection protocol, which will reduce latency and help in real-time command execution.&lt;/li&gt;&lt;li&gt;WebSockets keep a persistent connection and thus use resources more efficiently and cut the number of TCP connections used, in turn reducing the amount of network overhead.&lt;/li&gt;&lt;li&gt;The use of WebSockets fix issues that older protocols like SPDY had, and it&apos;s more compatible with modern web technologies.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The migration from SPDY to WebSockets in Kubernetes makes it much easier to communicate control messages, which will allow for better cluster management and stability.&lt;/p&gt;&lt;h2 id=&quot;heading-kep-4006&quot;&gt;Kep 4006&lt;/h2&gt;&lt;p&gt;The SPDY protocol was deprecated in 2016. In 2023, a proposal was opened in the Kubernetes community to change the protocol for commands like port-forward, attach, exec, and portforward that use upgraded connections.&lt;/p&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://github.com/kubernetes/enhancements/issues/4006&quot;&gt;Link to proposal&lt;/a&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://opengraph.githubassets.com/1/kubernetes/enhancements/issues/4006&quot;&gt;Link to issue&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;heading-implementation-overview&quot;&gt;Implementation Overview:&lt;/h3&gt;&lt;p&gt;WebSocket support in Kubernetes and kubectl starts with an HTTP handshake that upgrades the connection to a WebSocket, like as the SPDY. The WebSocket framing method provides improved technique for data framing for streams such as stdin, stdout, and stderr through framing with a channel identifier. This is an extension of the technique once used by SPDY through binary data framing.&lt;/p&gt;&lt;p&gt;As a fallback mechanism in case WebSocket support is not available on the server, a fallback to SPDY is utilized. The initial performance assessments demonstrated that WebSocket is as efficient as, or even a little better than, SPDY for standard kubectl operations, and continuing performance testing is intended to better performance across multiple use cases. There are many implications from this transition, including greater network compatibility in settings where strong proxy and firewall policies are in place, and better integration with external tools.&lt;/p&gt;&lt;h3 id=&quot;heading-api-server-changes&quot;&gt;API server changes:&lt;/h3&gt;&lt;p&gt;The API server now supports the WebSocket protocol by replacing SPDY with explicit headers to enable bidirectional streams. Additionally, the &lt;code&gt;StreamTranslatorProxy&lt;/code&gt; has been added to help create WebSocket connections from clients to internal SPDY connections in Kubernetes.&lt;/p&gt;&lt;p&gt;The management of WebSocket frames is critical, especially for the occasional use of the ping/pong frames to keep the WebSocket connection alive. This feature was first introduced in an alpha version that one could use through the feature gate for port forwarding Websockets within the API server and a corresponding flag in &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&quot;heading-client-kubectl-changes&quot;&gt;Client (&lt;code&gt;kubectl&lt;/code&gt;) Changes:&lt;/h3&gt;&lt;p&gt;On the client side, it added an upgrade to WebSocket for port forwarding, along with signing, response processing, and  data serialization and deserialization over the WebSocket. This gives the user the ability to enable WebSocket port forwarding  through environment variables. To make this transition easy, kubectl has also introduced mechanisms to handle errors and reliability, with error handling during its alpha and beta phases, along with a fallback to SPDY in case the WebSocket connection fails to ensure that service continuity is not lost.&lt;/p&gt;&lt;h2 id=&quot;heading-websockets-in-kubectl-port-forward-with-kind&quot;&gt;Websockets in &lt;code&gt;kubectl port-forward&lt;/code&gt; with Kind&lt;/h2&gt;&lt;h3 id=&quot;heading-setup-env&quot;&gt;Setup Env&lt;/h3&gt;&lt;p&gt;Before creating my environment, Docker, Kind, and Go have to be installed on the machine. All of these are needed for developing and deploying our Kubernetes cluster on our local machines. Let&apos;s set up the environment with Kubernetes 1.30.&lt;/p&gt;&lt;p&gt;Let&apos;s start by setting up the necessary environment variables and dependencies:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Set Go environment variables&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; GOPATH=&lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/go&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; GOROOT=/usr/&lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt;/go&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; PATH=&lt;span class=&quot;hljs-variable&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;hljs-variable&quot;&gt;$GOROOT&lt;/span&gt;/bin:&lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/bin&lt;span class=&quot;hljs-comment&quot;&gt;# Create a directory for Kubernetes source&lt;/span&gt;mkdir -p &lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/src/k8s.io&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/src/k8s.io&lt;span class=&quot;hljs-comment&quot;&gt;# Clone the Kubernetes repository&lt;/span&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/kubernetes/kubernetes.git&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; kubernetes&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;heading-install-dependencies-on-mac-m1&quot;&gt;Install Dependencies on Mac M1&lt;/h4&gt;&lt;p&gt;For those uses Mac with an M1 chip:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;brew install bashbrew install gnu-tar&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-building-the-kubernetes-and-kubectl-130-image&quot;&gt;Building the kubernetes and kubectl 1.30 image&lt;/h3&gt;&lt;p&gt;To build the images and kubectl, we need to have Go 1.22 or later. Then, execute some commands:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Checkout to the Kubernetes 1.30 release&lt;/span&gt;git checkout release-1.30&lt;span class=&quot;hljs-comment&quot;&gt;# Build a node image using Kind&lt;/span&gt;kind build node-image&lt;span class=&quot;hljs-comment&quot;&gt;# Build the kubectl in this version&lt;/span&gt;make kubectl KUBE_BUILD_PLATFORMS=darwin/arm64&lt;span class=&quot;hljs-comment&quot;&gt;# kubectl artifact output path : $GOPATH/kubernetes/_output/dockerized/bin/darwin/arm64/kubectl&lt;/span&gt;chmod +x &lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/kubernetes/_output/dockerized/bin/darwin/arm64/kubectl&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-kind-cluster-configs&quot;&gt;Kind Cluster Configs&lt;/h3&gt;&lt;p&gt;First, set up the kind cluster with 1.30 images by creating a &lt;code&gt;config.yaml&lt;/code&gt; file with featureGates &lt;code&gt;PortForwardWebsockets&lt;/code&gt; enabled:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Cluster&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;featureGates:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;PortForwardWebsockets&quot;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;nodes:&lt;/span&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;role:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;control-plane&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kindest/node:latest&lt;/span&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;role:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;worker&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kindest/node:latest&lt;/span&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;role:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;worker&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;kindest/node:latest&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-create-the-cluster&quot;&gt;Create the cluster&lt;/h3&gt;&lt;p&gt;When you have your configuration file set up you can add the cluster with the following command:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Change default kubeconfig path to have only with kind test cluster&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; KUBECONFIG=~/.kube/kind-kindkind create cluster --name kind-1-30 --config config.yaml&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-deploy-a-sample-app&quot;&gt;Deploy a sample app&lt;/h3&gt;&lt;p&gt;You can test your new WebSocket capability by deploying a simple application and using the cluster to connect by port forwarding:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/digitalocean/kubernetes-sample-apps.git&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; kubernetes-sample-appskubectl apply -k bookinfo-example/kustomize&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-forward-the-port-using-websockets&quot;&gt;Forward the Port Using WebSockets&lt;/h3&gt;&lt;p&gt;Enable the WebSocket port forwarding :&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; KUBECTL_PORT_FORWARD_WEBSOCKETS=&lt;span class=&quot;hljs-string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$GOPATH&lt;/span&gt;/kubernetes/_output/dockerized/bin/darwin/arm64/kubectl -v6 port-forward svc/productpage -n bookinfo 9080:9080&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, open &lt;code&gt;http://localhost:9080/&lt;/code&gt; in your browser. You should get responses from the echoserver.&lt;/p&gt;&lt;h3 id=&quot;heading-verifying-websocket-port-forwarding&quot;&gt;Verifying WebSocket Port Forwarding&lt;/h3&gt;&lt;p&gt;To confirm if WebSocket port forwarding is active, review the kubectl logs:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;This is the log with SDPY (default):&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.963059&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;466&lt;/span&gt;] curl -v -XPOST  -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;X-Stream-Protocol-Version: portforward.k8s.io&quot;&lt;/span&gt; -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;User-Agent: kubectl/v1.31.0 (darwin/arm64) kubernetes/cae35db&quot;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&apos;https://192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward&apos;&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.965663&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;510&lt;/span&gt;] HTTP Trace: Dial to tcp:&lt;span class=&quot;hljs-number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.68&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.109&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;6443&lt;/span&gt; succeedI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978422&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;553&lt;/span&gt;] POST https:&lt;span class=&quot;hljs-comment&quot;&gt;//192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward 101 Switching Protocols in 15 milliseconds&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978440&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;570&lt;/span&gt;] HTTP Statistics: DNSLookup &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ms Dial &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt; ms TLSHandshake &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ms Duration &lt;span class=&quot;hljs-number&quot;&gt;15&lt;/span&gt; msI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978444&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;577&lt;/span&gt;] Response Headers:I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978448&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Upgrade: SPDY/&lt;span class=&quot;hljs-number&quot;&gt;3.1&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978450&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     X-Stream-Protocol-Version: portforward.k8s.ioI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;32&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;50.978453&lt;/span&gt;   &lt;span class=&quot;hljs-number&quot;&gt;11299&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Connection: Upgrade&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;This is the logs with Websocket enabled:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.504179&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; tunneling_dialer.go:&lt;span class=&quot;hljs-number&quot;&gt;75&lt;/span&gt;] Before WebSocket Upgrade Connection...I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.504194&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;466&lt;/span&gt;] curl -v -XGET  -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;Sec-Websocket-Protocol: SPDY/3.1+portforward.k8s.io&quot;&lt;/span&gt; -H &lt;span class=&quot;hljs-string&quot;&gt;&quot;User-Agent: kubectl/v1.31.0 (darwin/arm64) kubernetes/cae35db&quot;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&apos;https://192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward&apos;&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.506351&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;510&lt;/span&gt;] HTTP Trace: Dial to tcp:&lt;span class=&quot;hljs-number&quot;&gt;192.168&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.68&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;.109&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;6443&lt;/span&gt; succeedI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519936&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;553&lt;/span&gt;] GET https:&lt;span class=&quot;hljs-comment&quot;&gt;//192.168.68.109:6443/api/v1/namespaces/bookinfo/pods/productpage-v1-64c658687d-5bhsg/portforward 101 Switching Protocols in 15 milliseconds&lt;/span&gt;I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519954&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;570&lt;/span&gt;] HTTP Statistics: DNSLookup &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ms Dial &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt; ms TLSHandshake &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt; ms ServerProcessing &lt;span class=&quot;hljs-number&quot;&gt;8&lt;/span&gt; ms Duration &lt;span class=&quot;hljs-number&quot;&gt;15&lt;/span&gt; msI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519958&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;577&lt;/span&gt;] Response Headers:I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519962&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Sec-Websocket-Accept: +AqmlgtoGPP/Rlfw6oAZMCN34SY=I0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519964&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Sec-Websocket-Protocol: SPDY/&lt;span class=&quot;hljs-number&quot;&gt;3.1&lt;/span&gt;+portforward.k8s.ioI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519966&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Upgrade: websocketI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519968&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; round_trippers.go:&lt;span class=&quot;hljs-number&quot;&gt;580&lt;/span&gt;]     Connection: UpgradeI0422 &lt;span class=&quot;hljs-number&quot;&gt;17&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;29&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;21.519973&lt;/span&gt;    &lt;span class=&quot;hljs-number&quot;&gt;7561&lt;/span&gt; tunneling_dialer.go:&lt;span class=&quot;hljs-number&quot;&gt;85&lt;/span&gt;] negotiated protocol: portforward.k8s.io&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The logs show that the protocol upgrade to SPDY via WebSocket was successful in Kubernetes 1.30&lt;/p&gt;&lt;h2 id=&quot;heading-kep-roadmap-port-forward-over-websockets&quot;&gt;KEP Roadmap Port Forward over Websockets&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Alpha Release (Kubernetes 1.30):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;WebSocket support added. Feature enabled via the &lt;code&gt;KUBECTL_PORT_FORWARD_WEBSOCKETS=true&lt;/code&gt; env var on the client side and  &lt;code&gt;PortForwardWebsockets&lt;/code&gt; alpha feature flag should be enabled on the API Server,.&lt;/p&gt;&lt;p&gt;Focus in the alpha phase would be testing from all perspectives, user feedback, and updating developer and user documentation for this alpha feature.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Beta Release(Target 1.31):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;More extensive testing for performance fine-tuning, user feedback, and updated documentation, the documentation will be updated regularly with the changes during the beta phase improvements.&lt;/p&gt;&lt;p&gt;User community feedback will be the basis in making the feature stable and usable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Stable Release (Target 1.32):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Ensure WebSocket support transitions from beta to stable with alpha feature flags removed and publish a deprecation notice to officially mark the transition away from SPDY-based port forwarding.&lt;/p&gt;&lt;h2 id=&quot;heading-kep-roadmap-remotecommand-over-websockets-exec-cp-attach&quot;&gt;KEP Roadmap RemoteCommand over websockets (exec, cp, attach)&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Beta release (Kubernetes 1.30):&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;WebSocket for &lt;code&gt;RemoteCommand&lt;/code&gt; (exec, cp, attach) will be on by default. The feature gates to the client and server are expected to ensure that the SPDY replacement has as little impact on the user as possible.&lt;/p&gt;&lt;p&gt;The biggest emphasis here is on making the changes transparent to the user and, for those who are wary of this new implementation, will always have the choice to disable it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Path to Stability:&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Post 1.30 and leading up to the 1.32 release, the focus will be on refinement in WebSocket functionality, all aimed at reaching zero known critical bugs.&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;And thats all, folks. This hopefully explains the important protocol communication change in Kubernetes. I&apos;ve had too many issues due to SPDY and lack of compatibility with new load balancers, proxies, etc., which was so complicated that I found myself at work. Now I see this migration from SPDY to WebSockets, so I&apos;m a bit excited.&lt;/p&gt;&lt;p&gt;Looking forward, I&apos;ll let you know if there are any updates about this KEP&lt;/p&gt;&lt;p&gt;Also, maybe there&apos;ll be errors or misinformation in this post; I&apos;m just human after all. Feel free to let me know if you find anything off on social media or post a comment here at the blog. Thanks!&lt;/p&gt;]]&gt;</hashnode:content><hashnode:coverImage>https://cdn.hashnode.com/res/hashnode/image/upload/v1714426536749/4c9481eb-57b4-43d8-93f7-c9debe4082a5.webp</hashnode:coverImage></item><item><title><![CDATA[Kubectl Port-forward Flow Explained]]></title><description><![CDATA[Introduction
Recently, I joined a discussion about how the kubectl port-forward command works, which caught my attention because I have an app that improves some aspects of the native kubectl port-forward. I made a public Mermaid chart to show the co...]]></description><link>https://blog.kftray.app/kubectl-port-forward-flow-explained</link><guid isPermaLink="true">https://blog.kftray.app/kubectl-port-forward-flow-explained</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[networking]]></category><category><![CDATA[Devops]]></category><category><![CDATA[SRE]]></category><dc:creator><![CDATA[Henrique Cavarsan]]></dc:creator><pubDate>Mon, 15 Apr 2024 02:12:50 GMT</pubDate><content:encoded>&lt;![CDATA[&lt;h2 id=&quot;heading-introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Recently, I joined a discussion about how the &lt;code&gt;kubectl port-forward&lt;/code&gt; command works, which caught my attention because I have an app that improves some aspects of the native &lt;code&gt;kubectl port-forward&lt;/code&gt;. I made a public Mermaid chart to show the complete process of executing this command. I included everything from the authentication phase to sending a request through localhost that goes to the target pod via SPDY.&lt;/p&gt;&lt;p&gt;I noticed this topic often comes up on various social networks, so I decided to write this article to share the diagram and explain the steps involved. I had two main reasons for writing this article:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;To share this information with those who are interested, especially since the official documentation doesn&apos;t cover this process in one place, making it hard to quickly get a full picture.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;To have a reference for myself for future use, so I can look back at this article when needed :D&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The diagram might be updated after its release to add more details or correct any mistakes I might have made (it happens :D). Feel free to point out any errors or add information if you find something wrong ;)&lt;/p&gt;&lt;p&gt;You can view and edit the full diagram here: &lt;a target=&quot;_blank&quot; href=&quot;https://www.mermaidchart.com/app/projects/a4818a6d-bba3-4ab8-bd02-18b6bf99795f/diagrams/3e4987a0-69eb-4585-8ef4-dcf3474fe25a/version/v0.1/edit&quot;&gt;Mermaid Link&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;heading-sequence-diagram-explained&quot;&gt;Sequence Diagram Explained&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146967414/3334949a-d8ac-4ff4-a0c2-9a188093d5e1.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;Full &lt;a target=&quot;_blank&quot; href=&quot;https://www.mermaidchart.com/raw/3e4987a0-69eb-4585-8ef4-dcf3474fe25a?theme=dark&amp;amp;version=v0.1&amp;amp;format=svg&quot;&gt;Mermaid SVG Link&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I will start by explaining each step in the diagram, grouped into 5 sections: Initialization, Authentication &amp;amp; Authorization, Information Retrieval for Pod, Port-forwarding Session Establishment, Configuring iptables for Port Forwarding and SPDY Session for Port Forwarding&lt;/p&gt;&lt;h3 id=&quot;heading-initialization&quot;&gt;Initialization&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146100123/4419c095-7e24-4928-8e0a-62968d94d4fe.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;The user initiates the port-forwarding process by executing &lt;code&gt;kubectl port-forward -n &amp;lt;namespace&amp;gt; &amp;lt;pod-name&amp;gt; &amp;lt;local-port&amp;gt;:&amp;lt;pod-port&amp;gt;&lt;/code&gt; via the CLI (Command-Line Interface).&lt;/p&gt;&lt;h3 id=&quot;heading-authentication-amp-authorization&quot;&gt;Authentication &amp;amp; Authorization&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146051834/07a573a3-c9cd-41f7-8a7c-98c9bda3084c.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;Upon receiving the command, the CLI sends a request to the Kubernetes API server to authenticate the user&apos;s tokens and verify permissions. This involves an initial connection establishment with a Bearer Token. The API server then verifies the token&apos;s validity and checks if the user has authorization to access the specified pod.&lt;/p&gt;&lt;h3 id=&quot;heading-information-retrieval-for-pod&quot;&gt;Information Retrieval for Pod&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146243159/9619270d-d416-4d18-9107-98c6086b7f85.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;To proceed with port-forwarding, the CLI retrieves essential details about the target pod by sending a GET request to the Kubernetes API server. Once received,&lt;/p&gt;&lt;h3 id=&quot;heading-port-forwarding-session-establishment&quot;&gt;Port-forwarding Session Establishment&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146307834/63cde060-654d-4c43-83a5-8fce95943ae4.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;The CLI initiates the port-forwarding session by sending a POST request to the Kubernetes API server, requesting the establishment of a port-forwarding connection for the specified pod. Upon receiving the request, the API server switches protocols to SPDY, establishing a persistent connection with multiplexing capabilities.&lt;/p&gt;&lt;h3 id=&quot;heading-configuring-iptables-for-port-forwarding&quot;&gt;Configuring iptables for Port Forwarding&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146366473/fed7567f-20d1-45b2-bae8-5711c3f22925.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;The Kubernetes API server instructs the Kubelet to configure iptables for port-forwarding. The Kubelet sets up iptables rules to redirect traffic from the specified pod port to the designated external port (local port in kubectl)&lt;/p&gt;&lt;h3 id=&quot;heading-spdy-session-for-port-forwarding&quot;&gt;SPDY Session for Port Forwarding&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146499506/11a7bf72-1649-419b-9573-94aff179c7db.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;With the port-forwarding session established, the user interacts with the pod&apos;s application by sending requests through the SPDY stream.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Feel free to edit the Mermaid chart here:&lt;/strong&gt; Mermaid Live Editor&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;This article explains the &lt;code&gt;kubectl port-forward&lt;/code&gt; command, detailing each step from starting up and logging in to transferring data. It covers authentication, authorization, and the SPDY connection to show how it works in Kubernetes.&lt;/p&gt;&lt;p&gt;While this article is meant to inform and not to promote, I&apos;d like to share a link to &lt;strong&gt;kftray&lt;/strong&gt;, a project I developed that adds new features and improvements to &lt;code&gt;kubectl port-forward&lt;/code&gt;. If you&apos;re interested, you can check out &lt;strong&gt;kftray&lt;/strong&gt; on GitHub here: &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;kftray on GitHub&lt;/a&gt;.&lt;/p&gt;]]&gt;</content:encoded><hashnode:content>&lt;![CDATA[&lt;h2 id=&quot;heading-introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Recently, I joined a discussion about how the &lt;code&gt;kubectl port-forward&lt;/code&gt; command works, which caught my attention because I have an app that improves some aspects of the native &lt;code&gt;kubectl port-forward&lt;/code&gt;. I made a public Mermaid chart to show the complete process of executing this command. I included everything from the authentication phase to sending a request through localhost that goes to the target pod via SPDY.&lt;/p&gt;&lt;p&gt;I noticed this topic often comes up on various social networks, so I decided to write this article to share the diagram and explain the steps involved. I had two main reasons for writing this article:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;To share this information with those who are interested, especially since the official documentation doesn&apos;t cover this process in one place, making it hard to quickly get a full picture.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;To have a reference for myself for future use, so I can look back at this article when needed :D&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The diagram might be updated after its release to add more details or correct any mistakes I might have made (it happens :D). Feel free to point out any errors or add information if you find something wrong ;)&lt;/p&gt;&lt;p&gt;You can view and edit the full diagram here: &lt;a target=&quot;_blank&quot; href=&quot;https://www.mermaidchart.com/app/projects/a4818a6d-bba3-4ab8-bd02-18b6bf99795f/diagrams/3e4987a0-69eb-4585-8ef4-dcf3474fe25a/version/v0.1/edit&quot;&gt;Mermaid Link&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;heading-sequence-diagram-explained&quot;&gt;Sequence Diagram Explained&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146967414/3334949a-d8ac-4ff4-a0c2-9a188093d5e1.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;Full &lt;a target=&quot;_blank&quot; href=&quot;https://www.mermaidchart.com/raw/3e4987a0-69eb-4585-8ef4-dcf3474fe25a?theme=dark&amp;amp;version=v0.1&amp;amp;format=svg&quot;&gt;Mermaid SVG Link&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I will start by explaining each step in the diagram, grouped into 5 sections: Initialization, Authentication &amp;amp; Authorization, Information Retrieval for Pod, Port-forwarding Session Establishment, Configuring iptables for Port Forwarding and SPDY Session for Port Forwarding&lt;/p&gt;&lt;h3 id=&quot;heading-initialization&quot;&gt;Initialization&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146100123/4419c095-7e24-4928-8e0a-62968d94d4fe.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;The user initiates the port-forwarding process by executing &lt;code&gt;kubectl port-forward -n &amp;lt;namespace&amp;gt; &amp;lt;pod-name&amp;gt; &amp;lt;local-port&amp;gt;:&amp;lt;pod-port&amp;gt;&lt;/code&gt; via the CLI (Command-Line Interface).&lt;/p&gt;&lt;h3 id=&quot;heading-authentication-amp-authorization&quot;&gt;Authentication &amp;amp; Authorization&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146051834/07a573a3-c9cd-41f7-8a7c-98c9bda3084c.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;Upon receiving the command, the CLI sends a request to the Kubernetes API server to authenticate the user&apos;s tokens and verify permissions. This involves an initial connection establishment with a Bearer Token. The API server then verifies the token&apos;s validity and checks if the user has authorization to access the specified pod.&lt;/p&gt;&lt;h3 id=&quot;heading-information-retrieval-for-pod&quot;&gt;Information Retrieval for Pod&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146243159/9619270d-d416-4d18-9107-98c6086b7f85.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;To proceed with port-forwarding, the CLI retrieves essential details about the target pod by sending a GET request to the Kubernetes API server. Once received,&lt;/p&gt;&lt;h3 id=&quot;heading-port-forwarding-session-establishment&quot;&gt;Port-forwarding Session Establishment&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146307834/63cde060-654d-4c43-83a5-8fce95943ae4.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;The CLI initiates the port-forwarding session by sending a POST request to the Kubernetes API server, requesting the establishment of a port-forwarding connection for the specified pod. Upon receiving the request, the API server switches protocols to SPDY, establishing a persistent connection with multiplexing capabilities.&lt;/p&gt;&lt;h3 id=&quot;heading-configuring-iptables-for-port-forwarding&quot;&gt;Configuring iptables for Port Forwarding&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146366473/fed7567f-20d1-45b2-bae8-5711c3f22925.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;The Kubernetes API server instructs the Kubelet to configure iptables for port-forwarding. The Kubelet sets up iptables rules to redirect traffic from the specified pod port to the designated external port (local port in kubectl)&lt;/p&gt;&lt;h3 id=&quot;heading-spdy-session-for-port-forwarding&quot;&gt;SPDY Session for Port Forwarding&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1713146499506/11a7bf72-1649-419b-9573-94aff179c7db.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;With the port-forwarding session established, the user interacts with the pod&apos;s application by sending requests through the SPDY stream.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Feel free to edit the Mermaid chart here:&lt;/strong&gt; Mermaid Live Editor&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;This article explains the &lt;code&gt;kubectl port-forward&lt;/code&gt; command, detailing each step from starting up and logging in to transferring data. It covers authentication, authorization, and the SPDY connection to show how it works in Kubernetes.&lt;/p&gt;&lt;p&gt;While this article is meant to inform and not to promote, I&apos;d like to share a link to &lt;strong&gt;kftray&lt;/strong&gt;, a project I developed that adds new features and improvements to &lt;code&gt;kubectl port-forward&lt;/code&gt;. If you&apos;re interested, you can check out &lt;strong&gt;kftray&lt;/strong&gt; on GitHub here: &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;kftray on GitHub&lt;/a&gt;.&lt;/p&gt;]]&gt;</hashnode:content><hashnode:coverImage>https://cdn.hashnode.com/res/hashnode/image/upload/v1713147022200/f7e3ec94-39c0-4583-9a42-e99ad035920b.png</hashnode:coverImage></item><item><title><![CDATA[Kubernetes Debugging: How to Handle Multiple kubectl port-forward from your System Tray]]></title><description><![CDATA[As a developer working with Kubernetes, I often find myself with multiple port-forward open sessions to debug pods across namespaces. Keeping track of all those terminals can get chaotic quickly. To help streamline my debugging workflow, I created an...]]></description><link>https://blog.kftray.app/kubernetes-debugging-handling-multiple-kubectl-port-forward-from-tray</link><guid isPermaLink="true">https://blog.kftray.app/kubernetes-debugging-handling-multiple-kubectl-port-forward-from-tray</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Devops]]></category><category><![CDATA[SRE]]></category><category><![CDATA[Developer]]></category><category><![CDATA[software development]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[devtools]]></category><dc:creator><![CDATA[Henrique Cavarsan]]></dc:creator><pubDate>Thu, 28 Mar 2024 02:34:58 GMT</pubDate><content:encoded>&lt;![CDATA[&lt;p&gt;As a developer working with Kubernetes, I often find myself with multiple port-forward open sessions to debug pods across namespaces. Keeping track of all those terminals can get chaotic quickly. To help streamline my debugging workflow, I created an open-source tool called KFTray that centralizes all your port forwards in a system tray. In this post, I demonstrate how can save developer time by comparing workflows with and without it.&lt;/p&gt;&lt;h2 id=&quot;heading-the-pain-of-kubernetes-debugging-with-kubectl-port-forward&quot;&gt;The Pain of Kubernetes Debugging with Kubectl Port Forward&lt;/h2&gt;&lt;p&gt;As an SRE, one of my primary responsibilities is debugging Kubernetes applications and infrastructure. The go-to tool for debugging pods in Kubernetes is kubectl port-forward. However, this utility has several limitations that often make the debugging process tedious and frustrating.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of Auto-Reconnect for Unstable Connections&lt;/strong&gt; The kubectl port-forward command does not automatically reconnect if the connection is interrupted for any reason. This means if there is an issue with the network or Kubernetes API server, the port forward will stop working, and I have to manually restart it. For debugging unstable applications, this missing feature results in a poor experience and loss of productivity.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;No UDP Support&lt;/strong&gt; Kubectl port-forward only supports TCP and does not natively support UDP. Many applications rely on UDP, so without support for UDP forwarding, debugging them is very difficult. We often have to modify applications to use TCP instead of UDP just for debugging purposes, which is far from ideal.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single Pod Forwarding Only&lt;/strong&gt; The kubectl port-forward command can only forward ports for a single pod at a time. When debugging complex, multi-pod applications, this means I frequently have to open multiple terminal tabs to port forward to each pod individually. This process is tedious, difficult to manage, and negatively impacts productivity during debugging sessions.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Difficulty Keeping Configurations in Sync&lt;/strong&gt; On our team, we have port forwarding configurations defined in a shared Git repository so we can standardize them across projects. However, with kubectl, the only way to use these shared configurations is to manually download them and run them locally. This makes it difficult to keep everyone&apos;s configurations in sync and up-to-date.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-introducing-kftray-a-developer-tool-for-kubernetes&quot;&gt;Introducing KFtray: A Developer Tool for Kubernetes&lt;/h2&gt;&lt;p&gt;As a developer working with Kubernetes, managing multiple port forwards for debugging applications can quickly become tedious and time-consuming. To simplify this process, I created KFtray, an open-source system tray application that provides a user-friendly interface for handling Kubernetes port forwarding.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Port Forward Management&lt;/strong&gt; KFtray allows developers to easily start, stop, and manage multiple port forwarding configurations simultaneously through an intuitive interface. Users can save configurations locally or sync them with a GitHub repository for team collaboration. KFtray automatically restarts pods as needed to maintain service continuity in the event of pod failure, improving reliability.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customized Configuration&lt;/strong&gt; Developers can customize KFtray to use a specified local IP address instead of the default localhost for port forwarding. They can also enable TCP/UDP proxy forwarding through Kubernetes for additional flexibility. KFtray uses each operating system&apos;s keyring to securely store confidential information like API keys.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Staying Up-to-Date with Git Synchronization&lt;/strong&gt; For teams collaborating on Kubernetes applications, keeping port forwarding configurations in sync can be challenging. KFtray addresses this through an optional Git synchronization feature. Developers can connect KFtray to a GitHub repository containing their port forwarding configurations. KFtray will then automatically pull the latest configurations from that repository on an adjustable polling schedule.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved Productivity&lt;/strong&gt; By simplifying the management of Kubernetes port forwarding, KFtray aims to make developers more productive. No longer do they have to manually restart pods or switch between multiple terminal windows to handle port forwarding. KFtray provides a single interface to control all their port forwarding needs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep your configurations updated with Github Sync&lt;/strong&gt; KFtray allows developers to easily store and manage port forward configurations from local files or GitHub repositories. This means users can save a configuration once and reload it whenever needed, reducing repetitive work. Developers can also share configurations with team members by committing them to a GitHub repo.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simultaneous Port Forwards&lt;/strong&gt; A key pain point KFtray solves is the ability to run multiple port forwards simultaneously. Without a tool like KFtray, developers have to manually run each port forward separately in their terminal. KFtray handles running all port forwards in the background, freeing up the developers terminal. This is especially useful when debugging microservice-based applications that have many component services.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Convenient System Tray&lt;/strong&gt; KFtray runs in the system tray, providing an easy way to manage all port forwards in one place. From the system tray menu, developers can start, stop, restart or remove port forwards. KFtray also displays the status of each port forward, indicating if it is running or stopped. This convenient UI enhances productivity by giving developers quick access to manage and monitor their port forwards.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-a-case-study-debugging-microservices-with-and-without-kftray&quot;&gt;A Case Study: Debugging Microservices With and Without KFtray&lt;/h2&gt;&lt;p&gt;As a developer focused on building microservices, I found the debugging process to be tedious and time-consuming. Each microservice has its own deployment, necessitating a separate port forward for debugging. Without an efficient tool to manage multiple port forwards, significant time was lost setting up and keeping configurations up to date.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The Situation Before&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Prior to using KFtray, I had to manually run a &lt;code&gt;kubectl port-forward&lt;/code&gt; command for each microservice I wanted to debug. If a pod restarted, the port forward would need to be re-established, causing further delays. When code was updated, the port forward configurations had to be manually updated to match, requiring diligent version control. Overall, managing the debugging infrastructure for microservices was inefficient and distracting.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A New Approach&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;KFtray simplified the debugging workflow by allowing me to define port forward configurations for each microservice in a central repository. With a single click, KFtray establishes all required port forwards, freeing me to focus on debugging my code. KFtray automatically reconnects port forwards when pods restart, and stays up-to-date with configuration changes through Git version control.&lt;/p&gt;&lt;h2 id=&quot;heading-get-started&quot;&gt;Get Started&lt;/h2&gt;&lt;h3 id=&quot;heading-installation&quot;&gt;Installation&lt;/h3&gt;&lt;p&gt;KFtray is available for macOS and Linux users via Homebrew, and directly from the GitHub releases page for other systems. Here&apos;s how you can get started:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;For macOS and Linux:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;brew tap hcavarsan/kftraybrew install --HEAD kftray&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For other systems, visit the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray/releases&quot;&gt;GitHub releases page&lt;/a&gt; for downloadable binaries.&lt;/p&gt;&lt;h2 id=&quot;heading-configuring-your-first-port-forward&quot;&gt;Configuring Your First Port Forward&lt;/h2&gt;&lt;p&gt;In a few simple steps, you can configure your first port forward:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Launch the application&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open the configuration panel from the tray icon&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a new configuration:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Give it a unique alias and set if you want to set the alias as domain to your forward&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Indicate if the configuration is for a port forward for a service (common use) or a proxy (port forward to an endpoint via a Kubernetes cluster).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Specify the Kubernetes context&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Define the namespace housing your service&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enter the service name&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Choose TCP or UDP&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set the local and remote port numbers&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configure a custom local IP address (optional)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activate Your Configuration&lt;/strong&gt;: With your configuration saved, simply click on the switch button in the main menu to start the port forward in a single por forward or in Start All to start all configurations at the same time .&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Demonstration:&lt;/p&gt;&lt;div class=&quot;embed-wrapper&quot;&gt;&lt;div class=&quot;embed-loading&quot;&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;a class=&quot;embed-card&quot; href=&quot;https://youtu.be/nqEhmcKeCc4&quot;&gt;https://youtu.be/nqEhmcKeCc4&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;heading-export-configurations-to-a-json-file&quot;&gt;Export configurations to a JSON file&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open the main menu in the footer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the &lt;code&gt;Export Local File&lt;/code&gt; option&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Choose a file name and location to save the JSON file&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The JSON file will contain all your current configurations&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You can then import this JSON file at any time to restore your configurations.&lt;/p&gt;&lt;p&gt;Example Json configuration File:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-json&quot;&gt;[ {  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;service&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;argocd-server&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;namespace&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;argocd&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;local_port&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;remote_port&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;8080&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;context&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;test-cluster&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;workload_type&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;service&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;protocol&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;remote_address&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;local_address&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;alias&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;argocd&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;domain_enabled&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt; }]&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;heading-sharing-the-configurations-through-git&quot;&gt;Sharing the configurations through Git&lt;/h2&gt;&lt;p&gt;now, with the local json saved, you can share your configurations with your team members by committing the JSON file to a Github repository. This allows for easy collaboration and synchronization of KFtray configurations across your team.&lt;/p&gt;&lt;p&gt;To import and sync your github configs in kftray:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open the application&apos;s main menu&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the button with github icon in the footer menu&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enter the URL of your Git repository and path containing the JSON file&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If your GitHub repository is private, you will need to enter the private token. Credentials are securely saved in the SO keyring (Keychain on macOS). Kftray does not store or save credentials in any local file; they are only stored in the local keyring.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the polling time for when Kftray will synchronize configurations and retrieve them from GitHub.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;KFtray will now sync with the Git repository to automatically import any new configurations or changes committed to the JSON file.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This allows you to quickly deploy any port forward changes to all team members. And if someone on your team adds a new configuration, it will be automatically synced to everyone else&apos;s KFtray.&lt;/p&gt;&lt;p&gt;Demonstration: d&lt;/p&gt;&lt;div class=&quot;embed-wrapper&quot;&gt;&lt;div class=&quot;embed-loading&quot;&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;a class=&quot;embed-card&quot; href=&quot;https://www.youtube.com/watch?v=BAdL7IzaEh8&quot;&gt;https://www.youtube.com/watch?v=BAdL7IzaEh8&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;heading-share-your-thoughts-help-us-enhance-kftray-with-your-feedback&quot;&gt;Share Your Thoughts: Help Us Enhance KFtray with Your Feedback!&lt;/h2&gt;&lt;p&gt;We hope our app makes your work easier. Any ideas for improvement? Were all ears!&lt;/p&gt;&lt;p&gt;Star us on &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;Github&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Kftray Website: &lt;a target=&quot;_blank&quot; href=&quot;https://kftray.app&quot;&gt;https://kftray.app&lt;/a&gt;&lt;/p&gt;]]&gt;</content:encoded><hashnode:content>&lt;![CDATA[&lt;p&gt;As a developer working with Kubernetes, I often find myself with multiple port-forward open sessions to debug pods across namespaces. Keeping track of all those terminals can get chaotic quickly. To help streamline my debugging workflow, I created an open-source tool called KFTray that centralizes all your port forwards in a system tray. In this post, I demonstrate how can save developer time by comparing workflows with and without it.&lt;/p&gt;&lt;h2 id=&quot;heading-the-pain-of-kubernetes-debugging-with-kubectl-port-forward&quot;&gt;The Pain of Kubernetes Debugging with Kubectl Port Forward&lt;/h2&gt;&lt;p&gt;As an SRE, one of my primary responsibilities is debugging Kubernetes applications and infrastructure. The go-to tool for debugging pods in Kubernetes is kubectl port-forward. However, this utility has several limitations that often make the debugging process tedious and frustrating.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of Auto-Reconnect for Unstable Connections&lt;/strong&gt; The kubectl port-forward command does not automatically reconnect if the connection is interrupted for any reason. This means if there is an issue with the network or Kubernetes API server, the port forward will stop working, and I have to manually restart it. For debugging unstable applications, this missing feature results in a poor experience and loss of productivity.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;No UDP Support&lt;/strong&gt; Kubectl port-forward only supports TCP and does not natively support UDP. Many applications rely on UDP, so without support for UDP forwarding, debugging them is very difficult. We often have to modify applications to use TCP instead of UDP just for debugging purposes, which is far from ideal.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single Pod Forwarding Only&lt;/strong&gt; The kubectl port-forward command can only forward ports for a single pod at a time. When debugging complex, multi-pod applications, this means I frequently have to open multiple terminal tabs to port forward to each pod individually. This process is tedious, difficult to manage, and negatively impacts productivity during debugging sessions.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Difficulty Keeping Configurations in Sync&lt;/strong&gt; On our team, we have port forwarding configurations defined in a shared Git repository so we can standardize them across projects. However, with kubectl, the only way to use these shared configurations is to manually download them and run them locally. This makes it difficult to keep everyone&apos;s configurations in sync and up-to-date.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-introducing-kftray-a-developer-tool-for-kubernetes&quot;&gt;Introducing KFtray: A Developer Tool for Kubernetes&lt;/h2&gt;&lt;p&gt;As a developer working with Kubernetes, managing multiple port forwards for debugging applications can quickly become tedious and time-consuming. To simplify this process, I created KFtray, an open-source system tray application that provides a user-friendly interface for handling Kubernetes port forwarding.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Port Forward Management&lt;/strong&gt; KFtray allows developers to easily start, stop, and manage multiple port forwarding configurations simultaneously through an intuitive interface. Users can save configurations locally or sync them with a GitHub repository for team collaboration. KFtray automatically restarts pods as needed to maintain service continuity in the event of pod failure, improving reliability.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customized Configuration&lt;/strong&gt; Developers can customize KFtray to use a specified local IP address instead of the default localhost for port forwarding. They can also enable TCP/UDP proxy forwarding through Kubernetes for additional flexibility. KFtray uses each operating system&apos;s keyring to securely store confidential information like API keys.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Staying Up-to-Date with Git Synchronization&lt;/strong&gt; For teams collaborating on Kubernetes applications, keeping port forwarding configurations in sync can be challenging. KFtray addresses this through an optional Git synchronization feature. Developers can connect KFtray to a GitHub repository containing their port forwarding configurations. KFtray will then automatically pull the latest configurations from that repository on an adjustable polling schedule.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved Productivity&lt;/strong&gt; By simplifying the management of Kubernetes port forwarding, KFtray aims to make developers more productive. No longer do they have to manually restart pods or switch between multiple terminal windows to handle port forwarding. KFtray provides a single interface to control all their port forwarding needs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep your configurations updated with Github Sync&lt;/strong&gt; KFtray allows developers to easily store and manage port forward configurations from local files or GitHub repositories. This means users can save a configuration once and reload it whenever needed, reducing repetitive work. Developers can also share configurations with team members by committing them to a GitHub repo.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simultaneous Port Forwards&lt;/strong&gt; A key pain point KFtray solves is the ability to run multiple port forwards simultaneously. Without a tool like KFtray, developers have to manually run each port forward separately in their terminal. KFtray handles running all port forwards in the background, freeing up the developers terminal. This is especially useful when debugging microservice-based applications that have many component services.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Convenient System Tray&lt;/strong&gt; KFtray runs in the system tray, providing an easy way to manage all port forwards in one place. From the system tray menu, developers can start, stop, restart or remove port forwards. KFtray also displays the status of each port forward, indicating if it is running or stopped. This convenient UI enhances productivity by giving developers quick access to manage and monitor their port forwards.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;heading-a-case-study-debugging-microservices-with-and-without-kftray&quot;&gt;A Case Study: Debugging Microservices With and Without KFtray&lt;/h2&gt;&lt;p&gt;As a developer focused on building microservices, I found the debugging process to be tedious and time-consuming. Each microservice has its own deployment, necessitating a separate port forward for debugging. Without an efficient tool to manage multiple port forwards, significant time was lost setting up and keeping configurations up to date.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The Situation Before&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Prior to using KFtray, I had to manually run a &lt;code&gt;kubectl port-forward&lt;/code&gt; command for each microservice I wanted to debug. If a pod restarted, the port forward would need to be re-established, causing further delays. When code was updated, the port forward configurations had to be manually updated to match, requiring diligent version control. Overall, managing the debugging infrastructure for microservices was inefficient and distracting.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A New Approach&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;KFtray simplified the debugging workflow by allowing me to define port forward configurations for each microservice in a central repository. With a single click, KFtray establishes all required port forwards, freeing me to focus on debugging my code. KFtray automatically reconnects port forwards when pods restart, and stays up-to-date with configuration changes through Git version control.&lt;/p&gt;&lt;h2 id=&quot;heading-get-started&quot;&gt;Get Started&lt;/h2&gt;&lt;h3 id=&quot;heading-installation&quot;&gt;Installation&lt;/h3&gt;&lt;p&gt;KFtray is available for macOS and Linux users via Homebrew, and directly from the GitHub releases page for other systems. Here&apos;s how you can get started:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;For macOS and Linux:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;brew tap hcavarsan/kftraybrew install --HEAD kftray&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For other systems, visit the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray/releases&quot;&gt;GitHub releases page&lt;/a&gt; for downloadable binaries.&lt;/p&gt;&lt;h2 id=&quot;heading-configuring-your-first-port-forward&quot;&gt;Configuring Your First Port Forward&lt;/h2&gt;&lt;p&gt;In a few simple steps, you can configure your first port forward:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Launch the application&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open the configuration panel from the tray icon&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a new configuration:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Give it a unique alias and set if you want to set the alias as domain to your forward&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Indicate if the configuration is for a port forward for a service (common use) or a proxy (port forward to an endpoint via a Kubernetes cluster).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Specify the Kubernetes context&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Define the namespace housing your service&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enter the service name&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Choose TCP or UDP&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set the local and remote port numbers&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configure a custom local IP address (optional)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activate Your Configuration&lt;/strong&gt;: With your configuration saved, simply click on the switch button in the main menu to start the port forward in a single por forward or in Start All to start all configurations at the same time .&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Demonstration:&lt;/p&gt;&lt;div class=&quot;embed-wrapper&quot;&gt;&lt;div class=&quot;embed-loading&quot;&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;a class=&quot;embed-card&quot; href=&quot;https://youtu.be/nqEhmcKeCc4&quot;&gt;https://youtu.be/nqEhmcKeCc4&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;heading-export-configurations-to-a-json-file&quot;&gt;Export configurations to a JSON file&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open the main menu in the footer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the &lt;code&gt;Export Local File&lt;/code&gt; option&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Choose a file name and location to save the JSON file&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The JSON file will contain all your current configurations&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You can then import this JSON file at any time to restore your configurations.&lt;/p&gt;&lt;p&gt;Example Json configuration File:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-json&quot;&gt;[ {  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;service&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;argocd-server&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;namespace&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;argocd&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;local_port&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;remote_port&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;8080&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;context&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;test-cluster&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;workload_type&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;service&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;protocol&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;remote_address&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;local_address&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;alias&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;argocd&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;domain_enabled&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt; }]&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;heading-sharing-the-configurations-through-git&quot;&gt;Sharing the configurations through Git&lt;/h2&gt;&lt;p&gt;now, with the local json saved, you can share your configurations with your team members by committing the JSON file to a Github repository. This allows for easy collaboration and synchronization of KFtray configurations across your team.&lt;/p&gt;&lt;p&gt;To import and sync your github configs in kftray:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open the application&apos;s main menu&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the button with github icon in the footer menu&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enter the URL of your Git repository and path containing the JSON file&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If your GitHub repository is private, you will need to enter the private token. Credentials are securely saved in the SO keyring (Keychain on macOS). Kftray does not store or save credentials in any local file; they are only stored in the local keyring.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Select the polling time for when Kftray will synchronize configurations and retrieve them from GitHub.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;KFtray will now sync with the Git repository to automatically import any new configurations or changes committed to the JSON file.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This allows you to quickly deploy any port forward changes to all team members. And if someone on your team adds a new configuration, it will be automatically synced to everyone else&apos;s KFtray.&lt;/p&gt;&lt;p&gt;Demonstration: d&lt;/p&gt;&lt;div class=&quot;embed-wrapper&quot;&gt;&lt;div class=&quot;embed-loading&quot;&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;div class=&quot;loadingRow&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;a class=&quot;embed-card&quot; href=&quot;https://www.youtube.com/watch?v=BAdL7IzaEh8&quot;&gt;https://www.youtube.com/watch?v=BAdL7IzaEh8&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;heading-share-your-thoughts-help-us-enhance-kftray-with-your-feedback&quot;&gt;Share Your Thoughts: Help Us Enhance KFtray with Your Feedback!&lt;/h2&gt;&lt;p&gt;We hope our app makes your work easier. Any ideas for improvement? Were all ears!&lt;/p&gt;&lt;p&gt;Star us on &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/hcavarsan/kftray&quot;&gt;Github&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Kftray Website: &lt;a target=&quot;_blank&quot; href=&quot;https://kftray.app&quot;&gt;https://kftray.app&lt;/a&gt;&lt;/p&gt;]]&gt;</hashnode:content><hashnode:coverImage>https://cdn.hashnode.com/res/hashnode/image/upload/v1711593237906/e50d5aa1-cad8-4d3e-9d26-751e75f18966.png</hashnode:coverImage></item></channel></rss>