diff --git a/.devcontainer/containerlab-dood/devcontainer-lock.json b/.devcontainer/containerlab-dood/devcontainer-lock.json new file mode 100644 index 0000000..75438be --- /dev/null +++ b/.devcontainer/containerlab-dood/devcontainer-lock.json @@ -0,0 +1,14 @@ +{ + "features": { + "ghcr.io/devcontainers/features/go:1": { + "version": "1.3.4", + "resolved": "ghcr.io/devcontainers/features/go@sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032", + "integrity": "sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032" + }, + "ghcr.io/devcontainers/features/node:2": { + "version": "2.0.0", + "resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f", + "integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f" + } + } +} diff --git a/.devcontainer/containerlab-dood/devcontainer.json b/.devcontainer/containerlab-dood/devcontainer.json new file mode 100644 index 0000000..70d567c --- /dev/null +++ b/.devcontainer/containerlab-dood/devcontainer.json @@ -0,0 +1,36 @@ +{ + "name": "containerlab-dood", + "image": "ghcr.io/srl-labs/containerlab/devcontainer-dood-slim:0.75.0", + "runArgs": [ + "--network=host", + "--pid=host", + "--privileged" + ], + "mounts": [ + "type=bind,src=/run/docker/netns,dst=/run/docker/netns", + "type=bind,src=/var/lib/docker,dst=/var/lib/docker", + "type=bind,src=/lib/modules,dst=/lib/modules", + "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached,readonly" + ], + "workspaceFolder": "${localWorkspaceFolder}", + "workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind", + "features": { + "ghcr.io/devcontainers/features/go:1": { + "version": "1.24.5" + }, + "ghcr.io/devcontainers/features/node:2": { + "version": "lts" + } + }, + "otherPortsAttributes": { + "onAutoForward": "ignore" + }, + "customizations": { + "vscode": { + "extensions": [ + "anthropic.claude-code" + ] + } + }, + "postCreateCommand": "bash .devcontainer/containerlab-dood/post-create.sh" +} \ No newline at end of file diff --git a/.devcontainer/containerlab-dood/post-create.sh b/.devcontainer/containerlab-dood/post-create.sh new file mode 100755 index 0000000..3322958 --- /dev/null +++ b/.devcontainer/containerlab-dood/post-create.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Starting post-create setup for containerlab-dood environment..." + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ARCH=$(uname -m) +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; +esac + +# Upgrade/install packages +echo "Upgrading/installing Debian packages..." +sudo apt update +sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y + +# Install Claude Code +echo "Installing Claude Code..." +curl -fsSL https://claude.ai/install.sh | bash + +# Install kubectl +echo "Installing kubectl..." +KUBECTL_VERSION=$(curl -fsSL https://dl.k8s.io/release/stable.txt) +curl -fsSL "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl" \ + | sudo tee /usr/local/bin/kubectl > /dev/null +sudo chmod +x /usr/local/bin/kubectl + +# Install kind for local Kubernetes +echo "Installing Kind..." +go install sigs.k8s.io/kind@latest + +# Install crane for pulling container images (Docker binaries in this environment +# panic on TLS 1.3 to Docker Hub due to an msft-golang/OpenSSL bug on ARM64) +echo "Installing crane..." +CRANE_VERSION=$(curl -fsSL https://api.github.com/repos/google/go-containerregistry/releases/latest | grep '"tag_name"' | cut -d'"' -f4) +curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VERSION}/go-containerregistry_Linux_${ARCH}.tar.gz" \ + | sudo tar xz -C /usr/local/bin crane + +# Verify installations +echo "" +echo "Verifying installations..." +echo "Go version: $(go version)" +echo "kubectl version: $(kubectl version --client 2>/dev/null || echo 'kubectl not installed')" +echo "kind version: $(kind version)" +echo "Docker version: $(docker --version)" + +echo "" +echo "Post-create setup completed successfully!" diff --git a/.devcontainer/README.md b/.devcontainer/galactic/README.md similarity index 100% rename from .devcontainer/README.md rename to .devcontainer/galactic/README.md diff --git a/.devcontainer/galactic/devcontainer-lock.json b/.devcontainer/galactic/devcontainer-lock.json new file mode 100644 index 0000000..adc9623 --- /dev/null +++ b/.devcontainer/galactic/devcontainer-lock.json @@ -0,0 +1,39 @@ +{ + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.8", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:c42fdefe6d737a3a6f61cc52b23c7c9a565d08cc4d9c303669a7cf2ee5fd81fc", + "integrity": "sha256:c42fdefe6d737a3a6f61cc52b23c7c9a565d08cc4d9c303669a7cf2ee5fd81fc" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.17.0", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c", + "integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.5", + "resolved": "ghcr.io/devcontainers/features/git@sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251", + "integrity": "sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251" + }, + "ghcr.io/devcontainers/features/go:1": { + "version": "1.3.4", + "resolved": "ghcr.io/devcontainers/features/go@sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032", + "integrity": "sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032" + }, + "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": { + "version": "1.3.1", + "resolved": "ghcr.io/devcontainers/features/kubectl-helm-minikube@sha256:bbe8adf6b37fff8c67412ab0a4579f4c2f30bbaba1d9a5cebd9e38bade54025b", + "integrity": "sha256:bbe8adf6b37fff8c67412ab0a4579f4c2f30bbaba1d9a5cebd9e38bade54025b" + }, + "ghcr.io/devcontainers/features/node:2": { + "version": "2.0.0", + "resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f", + "integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "1.8.0", + "resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511", + "integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/galactic/devcontainer.json similarity index 90% rename from .devcontainer/devcontainer.json rename to .devcontainer/galactic/devcontainer.json index 336365b..a1944ac 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/galactic/devcontainer.json @@ -111,17 +111,20 @@ "portsAttributes": { "8080": { "label": "Metrics", - "onAutoForward": "notify" + "onAutoForward": "silent" }, "8081": { "label": "Health", - "onAutoForward": "notify" + "onAutoForward": "silent" }, "9443": { "label": "Webhook", - "onAutoForward": "notify" + "onAutoForward": "silent" } }, + "otherPortsAttributes": { + "onAutoForward": "ignore" + }, "remoteUser": "vscode", "containerEnv": { "GOOS": "linux", @@ -129,9 +132,9 @@ }, "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind", - "source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached" + "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig.host,type=bind,consistency=cached,readonly" ], - "postCreateCommand": "bash .devcontainer/post-create.sh", + "postCreateCommand": "bash .devcontainer/galactic/post-create.sh", "runArgs": [ "--privileged", "--cap-add=NET_ADMIN", diff --git a/.devcontainer/post-create.sh b/.devcontainer/galactic/post-create.sh similarity index 68% rename from .devcontainer/post-create.sh rename to .devcontainer/galactic/post-create.sh index d541058..4360074 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/galactic/post-create.sh @@ -3,6 +3,14 @@ set -euo pipefail echo "Starting post-create setup for Galactic development environment..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Copy host gitconfig so VS Code can write its credential helper without hitting +# EBUSY (which occurs when the target path itself has an active bind mount). +if [[ -f /home/vscode/.gitconfig.host ]]; then + cp /home/vscode/.gitconfig.host /home/vscode/.gitconfig +fi + # Set up Go tools echo "Installing Go development tools..." go install golang.org/x/tools/gopls@latest @@ -16,25 +24,33 @@ make controller-gen kustomize setup-envtest # Install kind for local Kubernetes testing echo "Installing Kind..." -GO111MODULE=on go install sigs.k8s.io/kind@latest +go install sigs.k8s.io/kind@latest # Install protoc (Protocol Buffer compiler) echo "Installing protoc..." PROTOC_VERSION="25.1" -curl -LO "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" -sudo unzip -o protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local bin/protoc -sudo unzip -o protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local 'include/*' -rm -f protoc-${PROTOC_VERSION}-linux-x86_64.zip +PROTOC_ARCH=$(uname -m) +# protoc releases use aarch_64 (with underscore) for ARM64, unlike most other tools +case "$PROTOC_ARCH" in aarch64) PROTOC_ARCH="aarch_64" ;; esac +PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip" +curl -fsSLO "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP}" +sudo unzip -o "${PROTOC_ZIP}" -d /usr/local bin/protoc +sudo unzip -o "${PROTOC_ZIP}" -d /usr/local 'include/*' +rm -f "${PROTOC_ZIP}" # Install protoc-gen-go for Go protobuf generation echo "Installing protoc-gen-go..." go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest -# Install network tools -echo "Installing network tools..." -sudo apt-get update -sudo apt-get install -y \ +# Upgrade/install packages +echo "Upgrading/installing Ubuntu packages..." +sudo apt-get update -q +sudo apt-get install -y -q software-properties-common +sudo add-apt-repository -y ppa:apt-fast/stable +sudo apt-get update -q +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q apt-fast +sudo apt-fast install -y \ iproute2 \ iptables \ tcpdump \ @@ -60,10 +76,6 @@ echo "Generating Kubernetes manifests and DeepCopy methods..." cd /workspaces/galactic make manifests generate -# Set up git safe directory -echo "Configuring git safe directory..." -git config --global --add safe.directory /workspaces/galactic - # Install Claude Code CLI echo "Installing Claude Code..." curl -fsSL https://claude.ai/install.sh | bash diff --git a/README.md b/README.md index 4ebbe7d..8970943 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ Under the hood, Galactic uses Segment Routing over IPv6 (SRv6) for efficient, de ## Getting Started -See the [`lab/`](./lab/) directory for example topologies and the [DevContainer](./.devcontainer/) for development environment setup. +- See the [`lab/`](./lab/) directory for example topologies. +- See the [DevContainer](./.devcontainer/galactic/) for development environment setup. +- (macOS) See the [Containerlab DevContainer](./.devcontainer/containerlab-dood/) for running containerlab on ARM64. ## License diff --git a/lab/README.md b/lab/README.md index 0e2f879..db2cac2 100644 --- a/lab/README.md +++ b/lab/README.md @@ -4,9 +4,8 @@ Local development and integration-testing environments for [Galactic VPC](https: ``` lab/ -├── network/ # ContainerLab SRv6 underlay network -└── containers/ - └── kindest-node-galactic/ # Custom Kind node image with Galactic pre-installed +├── network/ # ContainerLab SRv6 underlay — standalone BGP/SRv6 network with FRR + GoBGP +└── gvpc/ # ContainerLab multi-cluster lab — three Kind clusters over an SRv6 transit mesh ``` --- @@ -38,76 +37,30 @@ make down # tear down --- -## `containers/kindest-node-galactic/` — Custom Kind Node Image +## `gvpc/` — Multi-Cluster GVPC Lab -A `kindest/node` image extended with the tooling and Kubernetes manifests needed to -run a full Galactic stack inside a [Kind](https://kind.sigs.k8s.io/) cluster. +A ContainerLab topology that connects three Kind clusters (`iad`, `sjc`, `infra`) over +an IPv6 SRv6 transit mesh. FRR runs as a node routing daemon on each cluster worker for +the eBGP underlay; GoBGP runs on `iad` and `sjc` workers to exchange L3VPN type-5 routes +with the `infra` route reflector over iBGP. Cilium, cert-manager, and Multus are +pre-installed on each cluster. -``` -kindest-node-galactic/ -├── Dockerfile -├── resources/ # Kubernetes manifests applied at cluster boot -│ ├── agent.k8s.yaml -│ ├── mqtt.k8s.yaml -│ ├── operator.k8s.yaml -│ └── router.k8s.yaml -└── scripts/ - └── install.sh # Installs Cilium, cert-manager, Multus, and Galactic -``` - -`install.sh` is invoked once per node after the cluster comes up. On the control-plane -node it applies each Kubernetes manifest in order (Cilium → cert-manager → Multus → -MQTT → Galactic operator → router → agent). On worker nodes it loads kernel modules, -sets SRv6 sysctls, and drops in the CNI binaries. +See [`gvpc/README.md`](gvpc/README.md) for topology details, addressing, and +verification commands. ### Prerequisites +- ContainerLab ≥ 0.54 - Docker -- [`kind`](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) -- Linux kernel with `vrf` module and SRv6 support (or a VM with those features) +- `kind` CLI +- Host kernel with SRv6 support -### Build +### Quick start (gvpc) ```bash -cd containers/kindest-node-galactic -docker build -t kindest/node:galactic . -``` - -### Use with Kind - -Reference the custom image in your Kind cluster config: - -```yaml -# kind-config.yaml -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: - - role: control-plane - image: kindest/node:galactic - - role: worker - image: kindest/node:galactic - - role: worker - image: kindest/node:galactic -``` - -```bash -kind create cluster --config kind-config.yaml -``` - -After the cluster is up, run `install.sh` on each node: - -```bash -for node in $(kind get nodes); do - docker exec "$node" /galactic/scripts/install.sh -done +cd gvpc +make up # build Kind node image, apply host sysctls, deploy lab +make underlay # apply FRR DaemonSets to all three clusters +make overlay # pull GoBGP image, load into clusters, apply DaemonSets +make down # tear down ``` - -### Component versions (pinned in `scripts/install.sh`) - -| Component | Version | -|--------------|----------| -| Cilium CLI | v0.18.8 | -| cert-manager | v1.19.1 | -| Multus CNI | v4.2.3 | -| CNI plugins | v1.8.0 | -| Galactic | v0.0.5 | diff --git a/lab/gvpc/Makefile b/lab/gvpc/Makefile index ce953b7..56882a7 100644 --- a/lab/gvpc/Makefile +++ b/lab/gvpc/Makefile @@ -2,7 +2,11 @@ LAB_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) LAB := $(shell awk '/^name:/ {print $$2; exit}' *.clab.yaml) TOPO := $(wildcard *.clab.yaml) -.PHONY: help build up down reload inspect graph host-setup underlay overlay clean +KINDEST_VER := v1.35.1 +ARCH := $(shell uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + +.PHONY: help build up down reload inspect graph host-setup underlay overlay test test-bgp-transit test-bgp-underlay \ + test-srv6 test-l3vpn clean .DEFAULT_GOAL := help @@ -12,7 +16,14 @@ help: ## Show available targets | sort build: ## Build container images (Kind node) - docker build -t kindest/node:galactic $(LAB_DIR)containers/kindest-node-galactic + @if ! docker image inspect kindest/node:$(KINDEST_VER) >/dev/null 2>&1; then \ + crane pull --platform linux/$(ARCH) \ + kindest/node:$(KINDEST_VER) /tmp/kindest-node-$(KINDEST_VER).tar; \ + docker load -i /tmp/kindest-node-$(KINDEST_VER).tar; \ + rm /tmp/kindest-node-$(KINDEST_VER).tar; \ + fi + docker build --pull=false --build-arg KINDEST_VER=$(KINDEST_VER) \ + -t kindest/node:galactic $(LAB_DIR)containers/kindest-node-galactic up: build host-setup ## Build the Kind node image and deploy the lab sudo containerlab deploy -t $(TOPO) @@ -40,6 +51,33 @@ overlay: ## Pull GoBGP image, load into clusters, and apply DaemonSets to iad an kind load docker-image osrg/gobgp:latest --name sjc ./scripts/install-overlay.sh +test: test-bgp-transit test-bgp-underlay test-srv6 test-l3vpn + +test-bgp-transit: ## Verify transit BGP sessions routes + @echo "=== Transit router BGP (iBGP full mesh) ===" + @for r in tr1 tr2 tr3 tr4; do \ + echo "--- $$r ---"; \ + docker exec clab-gvpc-$$r vtysh -c "show bgp ipv6 unicast summary"; \ + done + +test-bgp-underlay: ## Verify underlay BGP sessions + @echo "=== Worker underlay eBGP ===" + docker exec iad-control-plane kubectl exec -n iad-underlay ds/iad-underlay -- vtysh -c "show bgp ipv6 unicast summary" + docker exec sjc-control-plane kubectl exec -n sjc-underlay ds/sjc-underlay -- vtysh -c "show bgp ipv6 unicast summary" + +test-srv6: ## Verify SRv6 routes + @echo "=== SRv6 forwarding prefixes on tr1 ===" + docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff01::/48" + docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff02::/48" + docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff03::/48" + +test-l3vpn: ## GoBGP L3VPN + @echo "=== GoBGP L3VPN neighbors and RIB ===" + docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp neighbor + docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp neighbor + docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp global rib -a vpnv6 + docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp global rib -a vpnv6 + clean: down ## Destroy the lab, remove ContainerLab state, and delete container images docker rmi kindest/node:galactic || true docker rmi osrg/gobgp:latest || true diff --git a/lab/gvpc/README.md b/lab/gvpc/README.md index ea68c89..4e8c640 100644 --- a/lab/gvpc/README.md +++ b/lab/gvpc/README.md @@ -132,6 +132,8 @@ gvpc/ │ └── tr4/ frr.conf startup.sh ├── group_files/ │ ├── common/ hosts vtysh.conf startup-lib.sh +│ ├── control/ daemons +│ ├── pe/ daemons │ └── transit/ daemons └── scripts/ ├── host-setup.sh @@ -166,7 +168,7 @@ make overlay # apply GoBGP DaemonSets to iad and sjc clusters | `graph` | Generate a draw.io diagram for the topology | | `host-setup` | Apply required host sysctls (IPv6 forwarding etc.) | | `underlay` | Apply FRR DaemonSets to all three clusters | -| `overlay` | Apply GoBGP DaemonSets to iad and sjc clusters | +| `overlay` | Pull GoBGP image, load into clusters, apply DaemonSets | | `clean` | Destroy lab, remove state, and delete the Kind node image | ## Verification diff --git a/lab/gvpc/containers/kindest-node-galactic/Dockerfile b/lab/gvpc/containers/kindest-node-galactic/Dockerfile index 941a08e..55c38af 100644 --- a/lab/gvpc/containers/kindest-node-galactic/Dockerfile +++ b/lab/gvpc/containers/kindest-node-galactic/Dockerfile @@ -1,4 +1,6 @@ -FROM kindest/node:v1.35.1 +ARG KINDEST_VER=v1.35.1 +# ARG is assigned a default value, but we pass the desired one from the Makefile +FROM kindest/node:${KINDEST_VER} SHELL ["/bin/bash", "-eo", "pipefail", "-c"] @@ -10,3 +12,9 @@ RUN apt-get update \ WORKDIR /galactic COPY --chmod=0755 scripts/ ./scripts/ + +# Wrap kubectl to work around OrbStack DooD: kind runs `kubectl apply` for the +# StorageClass step immediately after kubeadm init, but the apiserver briefly +# goes offline while OrbStack's bridge finishes coming up, causing worker nodes +# to never reach kubeadm join. The wrapper polls /healthz before passing through. +COPY --chmod=0755 kubectl-wrapper /usr/local/bin/kubectl diff --git a/lab/gvpc/containers/kindest-node-galactic/kubectl-wrapper b/lab/gvpc/containers/kindest-node-galactic/kubectl-wrapper new file mode 100644 index 0000000..6f80184 --- /dev/null +++ b/lab/gvpc/containers/kindest-node-galactic/kubectl-wrapper @@ -0,0 +1,9 @@ +#!/bin/bash +# In OrbStack DooD, the kubelet briefly restarts the apiserver static pod after +# kubeadm init exits, creating a window where nothing listens on 6443. Kind +# runs the StorageClass step immediately after kubeadm and hits this window. +# Poll /healthz until the apiserver is actually accepting connections first. +until curl -sk https://127.0.0.1:6443/healthz >/dev/null 2>&1; do + sleep 1 +done +exec /usr/bin/kubectl --server=https://127.0.0.1:6443 "$@" diff --git a/lab/gvpc/containers/kindest-node-galactic/scripts/install.sh b/lab/gvpc/containers/kindest-node-galactic/scripts/install.sh index bc3bce7..6fc69ca 100644 --- a/lab/gvpc/containers/kindest-node-galactic/scripts/install.sh +++ b/lab/gvpc/containers/kindest-node-galactic/scripts/install.sh @@ -18,7 +18,7 @@ if hostname |grep -q control-plane; then # control-plane # Cilium curl -L https://github.com/cilium/cilium-cli/releases/download/${CILIUM_VERSION}/cilium-linux-${ARCH}.tar.gz |tar xvfz - -C /usr/local/bin && chmod +x /usr/local/bin/cilium - cilium install --set cni.exclusive=false && cilium status --wait + cilium install --set cni.exclusive=false --set kubeProxyReplacement=true && cilium status --wait if ! hostname |grep -q infra; then # Cert Manager diff --git a/lab/gvpc/gvpc.clab.yaml b/lab/gvpc/gvpc.clab.yaml index 0821c2d..f4e0069 100644 --- a/lab/gvpc/gvpc.clab.yaml +++ b/lab/gvpc/gvpc.clab.yaml @@ -31,7 +31,7 @@ topology: extras: k8s_kind: deploy: - wait: 60s + wait: 0s kubeconfig: iad.kubeconfig iad-control-plane: @@ -86,7 +86,7 @@ topology: extras: k8s_kind: deploy: - wait: 60s + wait: 0s kubeconfig: sjc.kubeconfig sjc-control-plane: @@ -141,7 +141,7 @@ topology: extras: k8s_kind: deploy: - wait: 60s + wait: 0s kubeconfig: infra.kubeconfig infra-control-plane: diff --git a/lab/gvpc/resources/iad-overlay.k8s.yaml b/lab/gvpc/resources/iad-overlay.k8s.yaml index 348e015..3c7fbf0 100644 --- a/lab/gvpc/resources/iad-overlay.k8s.yaml +++ b/lab/gvpc/resources/iad-overlay.k8s.yaml @@ -27,7 +27,7 @@ data: local-address = "fc00:0:2::1" [[neighbors.afi-safis]] [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" + afi-safi-name = "l3vpn-ipv6-unicast" --- apiVersion: apps/v1 diff --git a/lab/gvpc/resources/infra-control-plane.k8s.yaml b/lab/gvpc/resources/infra-control-plane.k8s.yaml index b9c642c..d944018 100644 --- a/lab/gvpc/resources/infra-control-plane.k8s.yaml +++ b/lab/gvpc/resources/infra-control-plane.k8s.yaml @@ -47,6 +47,8 @@ data: description tr3-facing ! + ipv6 route 2001:db8:ff03::/48 Null0 + ! router bgp 65000 bgp router-id 10.255.255.1 no bgp default ipv4-unicast @@ -63,10 +65,11 @@ data: address-family ipv6 unicast neighbor eth1 activate neighbor eth1 allowas-in 1 + network 2001:db8:ff03::/48 network fc00:0:4::1/128 exit-address-family ! - address-family ipv4 vpn + address-family ipv6 vpn neighbor fc00:0:2::1 activate neighbor fc00:0:2::1 route-reflector-client neighbor fc00:0:3::1 activate diff --git a/lab/gvpc/resources/sjc-overlay.k8s.yaml b/lab/gvpc/resources/sjc-overlay.k8s.yaml index 84a5188..98d720f 100644 --- a/lab/gvpc/resources/sjc-overlay.k8s.yaml +++ b/lab/gvpc/resources/sjc-overlay.k8s.yaml @@ -27,7 +27,7 @@ data: local-address = "fc00:0:3::1" [[neighbors.afi-safis]] [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" + afi-safi-name = "l3vpn-ipv6-unicast" --- apiVersion: apps/v1 diff --git a/lab/network/Makefile b/lab/network/Makefile index d0121c4..fd162ef 100644 --- a/lab/network/Makefile +++ b/lab/network/Makefile @@ -1,7 +1,8 @@ LAB := $(shell awk '/^name:/ {print $$2; exit}' *.clab.yaml) TOPO := $(wildcard *.clab.yaml) +ARCH := $(shell uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') -.PHONY: help build up down reload inspect graph host-setup clean +.PHONY: help pull-base build up down reload inspect graph host-setup clean .DEFAULT_GOAL := help @@ -10,8 +11,16 @@ help: ## Show available targets | awk 'BEGIN {FS = ":.*##"}; {printf " \033[36m%-24s\033[0m %s\n", $$1, $$2}' \ | sort -build: ## Build the custom GoBGP + FRR image - docker build -t gobgp-pe:latest containers/gobgp-pe +pull-base: ## Pull the base image for gobgp-pe (uses crane; Docker Hub TLS 1.3 panics in OrbStack's msft-golang Docker builds) + @if ! docker image inspect debian:bookworm-slim >/dev/null 2>&1; then \ + crane pull --platform linux/$(ARCH) \ + debian:bookworm-slim /tmp/debian-bookworm-slim.tar; \ + docker load -i /tmp/debian-bookworm-slim.tar; \ + rm /tmp/debian-bookworm-slim.tar; \ + fi + +build: pull-base ## Build the custom GoBGP + FRR image + docker build --pull=false -t gobgp-pe:latest containers/gobgp-pe up: build host-setup ## Build the image and deploy the lab sudo containerlab deploy -t $(TOPO) diff --git a/lab/network/scripts/host-setup.sh b/lab/network/scripts/host-setup.sh index aa0f637..963f676 100755 --- a/lab/network/scripts/host-setup.sh +++ b/lab/network/scripts/host-setup.sh @@ -3,21 +3,18 @@ set -euo pipefail -DUALSTACK=false PERSIST=true SYSCTL_CONF=/etc/sysctl.d/99-clab.conf usage() { - echo "Usage: $0 [--dualstack] [--no-persist]" + echo "Usage: $0 [--no-persist]" echo "" - echo " --dualstack Enable IPv4 forwarding in addition to IPv6" echo " --no-persist Apply sysctls now but do not write to ${SYSCTL_CONF}" exit 1 } for arg in "$@"; do case $arg in - --dualstack) DUALSTACK=true ;; --no-persist) PERSIST=false ;; --help|-h) usage ;; *) echo "Unknown argument: $arg"; usage ;; @@ -30,7 +27,6 @@ if [ "$(id -u)" -ne 0 ]; then fi echo "==> Containerlab host setup" -echo " dualstack : ${DUALSTACK}" echo " persist : ${PERSIST}" echo "" @@ -38,13 +34,8 @@ echo "--> Enabling IPv6 forwarding" sysctl -w net.ipv6.conf.all.forwarding=1 sysctl -w net.ipv6.conf.default.forwarding=1 -if [ "$DUALSTACK" = true ]; then - echo "--> Enabling IPv4 forwarding (dual-stack)" - sysctl -w net.ipv4.ip_forward=1 -else - echo "--> Disabling IPv4 forwarding (IPv6-only lab)" - sysctl -w net.ipv4.ip_forward=0 -fi +echo "--> Enabling IPv4 forwarding" +sysctl -w net.ipv4.ip_forward=1 if [ "$PERSIST" = true ]; then echo "--> Writing ${SYSCTL_CONF}" @@ -53,11 +44,7 @@ if [ "$PERSIST" = true ]; then echo "# Generated by host-setup.sh" echo "net.ipv6.conf.all.forwarding=1" echo "net.ipv6.conf.default.forwarding=1" - if [ "$DUALSTACK" = true ]; then - echo "net.ipv4.ip_forward=1" - else - echo "net.ipv4.ip_forward=0" - fi + echo "net.ipv4.ip_forward=1" } >"${SYSCTL_CONF}" echo " written." fi