Almost 25 years after its creation, IPv6 adoption is steadily (if slowly) growing. According to Google's statistics, availability of IPv6 connectivity has grown to 40% of Google users worldwide. In the cloud native space, the vast majority of users did not require the infinite IP space that IPv6 provided. It is, however, changing. Telcos and carriers, large webscalers, IoT organizations : they all require the scale that IPv6 provides. Kubernetes' IPv6 support has improved over the years, with an important milestone arriving last year: Dual-stack IPv4/IPv6 Networking Reached General Availability in Kubernetes 1.23. It means that Kubernetes is not only IPv6-ready but it also provides a transitional pathway from IPv4 to IPv6. With Dual Stack, each pod is allocated both an IPv4 and an IPv6 address, so it can communicate both with IPv6 systems and the legacy apps and cloud services that use IPv4. In order to run Dual Stack on Kubernetes, you need a CNI that supports it: of course, Cilium does. In order to operate Dual Stack and manage the added complexity that comes with IPv6 (128-bit addresses are not exactly easy to remember), you should consider an observability platform like Hubble. This blog post will walk you through how to deploy a IPv4/IPv6 Dual Stack Kubernetes cluster and install Cilium and Hubble to benefit from their networking and observability capabilities. The very short version of this tutorial can be seen below in 43 seconds. If you want to do it yourself, follow the instructions further below. Here are my step by step instructions. To make it easy, we'll be leveraging Kind so that you can test it yourself. If you already have a Dual Stack cluster, you can skip to Step 2. Step 1: Deploy a Dual Stack Kubernetes Cluster First, deploy a Kubernetes cluster with Kind (click on the link to install it if you don't have it already). You can use the following YAML configuration (save it as cluster.yaml for example): The important parameters here are: disableDefaultCNI is set to true as Cilium will be deployed instead of the default CNI. ipFamily set to dual for Dual Stack (IPv4 and IPv6 support). More details can be found on the official Kubernetes docs. apiServerAddress set to 127.0.0.1 (This is the listen address on the host for Kubernetes API Server. Because IPv6 port forwards don’t work on Docker on Windows or Mac, you need to use an IPv4 port forward. It is not needed on Linux. Read more on the kind docs). Deploy the cluster and you should be up and running in a couple of minutes: The first thing to notice is that the nodes themselves pick up both an IPv4 and an IPv6 address: With the following command, you can see the PodCIDRs from which IPv4 and IPv6 addresses will be allocated to your Pods. Step 2: Install Cilium in Dual Stack mode The next step is to install Cilium. That's required for IP address management and connectivity and also for flow visibility (as the observability platform Hubble is built on top of Cilium). If you don't have the Cilium CLI, install and download it via the official Cilium docs. The CLI itself is an easy tool to install and manage Cilium. Once that's installed, go ahead and enable Cilium in dual stack mode. Simply set the parameter --helm-set ipv6.enabled to true (IPv6 is disabled by default). Note we are not disabling IPv4 (it's enabled by default) and will therefore be operating in Dual Stack mode. By this stage, when you run cilium status, it should look like this: Step 3: Enable Hubble Again here, if you don't have it already, I recommend you download and install the Hubble client (follow the official Hubble docs). It's a single command to enable Hubble. Don't forget the --ui if you're planning on visualizing the flow on the Hubble UI. You're now ready to launch the Hubble UI with the following command: A browser should launch with the Hubble UI. Select the default namespace for now. Leave the terminal running and move to a new one where you're going to deploy applications to generate some traffic flow. Step 4: Deploy Applications Let's start by deploying a client, named pod-worker, with this simple Pod manifest. I use the netshoot image in this example but you can use other images if you prefer. Once you deploy it, notice it has two IP addresses allocated - IPv4 and IPv6. You can directly get the IPv6 address with this command. Deploy another Pod (named pod-worker2) to verify successfully IPv6 connectivity. Both pods are manually pinned to different hosts by using spec.nodeName. As a result, the successful ping below showed successful IPv6 connectivity between Pods on different nodes. You can now test Pod to Service connectivity. We'll use an echo server (An echo server is a server that replicates the request sent by the client and sends it back). You can use this manifest (link to GitHub) (a slightly modified and simplified version of this echo-server manifest). Notice the ipFamilyPolicy and ipFamilies Service settings required for IPv6 in this excerpt from the manifest: Deploy it: Check the echoserver Service: you should see both IPv4 and IPv6 addresses allocated on the IPs line. AAAA records are assigned automatically to Services. Now you can just use nslookup -q=AAAA to make an IPv6 DNS query. Finally, you can execute a curl request over IPv6 only, using the -6 option. Both curl requests to the AAAA record or the IP address are executed successfully. Step 5: Verify Flows on Hubble Let's go back to the Hubble UI. You should be able to see all your flows. To narrow down the results, you can filter based on the name of the pod to only see the flows you are interested in. Hopefully you, like me, find this pretty cool: you can troubleshoot IPv6 connectivity issues without having to remember 128-bit addresses! If you update the columns like I did, you can see some fields that are hidden by default: If you prefer using the CLI, then that's absolutely fine. Stop the terminal where you were running cilium hubble ui and instead we're going to be running hubble observe. If you run a continuous IPv6 ping from pod-worker to pod-worker2, you can easily see these flows with hubble observe --ipv6 --from-pod pod-worker: You can even print the node where the Pods are running with the --print-node-name: You should see both HTTP and ICMPv6 flows (if not, simply re-run a curl from the pod-worker shell). Notice that, in the code output above, we had IPv6 addresses instead of the Pod name. By default, Hubble will translate IP address to logical names such as Pod name or FQDN. You can disable it if you want the source and destination IPv6 addresses by using the --ip-translation=false command. If you just want to see your ping messages, you can simply filter based on the protocol: And that's it! Hopefully you can see how running IPv6 on Kubernetes does not need to be an operational nightmare, if you have the right tools in place. Feel free to get in touch with us, to schedule an IPv6 demo of Isovalent Cilium Enterprise and learn about features such as Segment Routing v6 (SRv6). Thanks for reading. Learn More Isovalent Resources: Learn more about Isovalent Cilium Enterprise Cilium and eBPF Resources: Join our awesome community Cilium Open Source Project eBPF Community Resources eBPF eCHO show with Duffie Cooley on Kubernetes IPv4/IPv6 Dual Stack with Cilium (and the inspiration behind this post!)