From 840bd2e12a188c0f97626e32244aa765dffe9b9f Mon Sep 17 00:00:00 2001 From: Sergio Talens-Oliag Date: Sun, 11 May 2025 19:56:07 +0200 Subject: [PATCH] Initial commit. --- .gitignore | 2 + README.md | 4 ++ apps/dummyhttp/kustomization.yaml | 33 +++++++++++++ apps/reloader/kustomization.yaml | 17 +++++++ bin/arkade-install.sh | 37 +++++++++++++++ bin/create-vcluster.sh | 75 +++++++++++++++++++++++++++++ bin/install-vcluster-apps.sh | 30 ++++++++++++ bin/vcluster-dummyhttp-secret.sh | 78 +++++++++++++++++++++++++++++++ vcluster/ingress_route_tcp.yaml | 15 ++++++ vcluster/vcluster.yaml | 31 ++++++++++++ 10 files changed, 322 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 apps/dummyhttp/kustomization.yaml create mode 100644 apps/reloader/kustomization.yaml create mode 100755 bin/arkade-install.sh create mode 100755 bin/create-vcluster.sh create mode 100755 bin/install-vcluster-apps.sh create mode 100755 bin/vcluster-dummyhttp-secret.sh create mode 100644 vcluster/ingress_route_tcp.yaml create mode 100644 vcluster/vcluster.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a58f553 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dummyhttp-secret.yaml +dummyhttp-sealed-secret.yaml diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ac4312 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# vCluster + +This repository contains scripts and templates to test `vcluster` on a `k3d` cluster (we are going to use the one +created for this [post](https://blogops.mixinet.net/posts/gitops/argocd-autopilot/)). diff --git a/apps/dummyhttp/kustomization.yaml b/apps/dummyhttp/kustomization.yaml new file mode 100644 index 0000000..e268cf0 --- /dev/null +++ b/apps/dummyhttp/kustomization.yaml @@ -0,0 +1,33 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - https://forgejo.mixinet.net/blogops/argocd-applications.git//dummyhttp/?ref=dummyhttp-v1.0.0 +# Add the config map +configMapGenerator: + - name: dummyhttp-configmap + literals: + - CM_VAR="Vcluster Test Value" + behavior: create + options: + disableNameSuffixHash: true +patches: + # Change the ingress host name + - target: + kind: Ingress + name: dummyhttp + patch: |- + - op: replace + path: /spec/rules/0/host + value: vcluster-dummyhttp.lo.mixinet.net + # Add reloader annotations -- it will only work if we install reloader on the + # virtual cluster, as the one on the host cluster doesn't see the vcluster + # deployment objects + - target: + kind: Deployment + name: dummyhttp + patch: |- + - op: add + path: /metadata/annotations + value: + reloader.stakater.com/auto: "true" + reloader.stakater.com/rollout-strategy: "restart" diff --git a/apps/reloader/kustomization.yaml b/apps/reloader/kustomization.yaml new file mode 100644 index 0000000..453101d --- /dev/null +++ b/apps/reloader/kustomization.yaml @@ -0,0 +1,17 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: kube-system +resources: +- github.com/stakater/Reloader/deployments/kubernetes/?ref=v1.4.2 +patches: +# Add flags to reload workloads when ConfigMaps or Secrets are created or deleted +- target: + kind: Deployment + name: reloader-reloader + patch: |- + - op: add + path: /spec/template/spec/containers/0/args + value: + - '--reload-on-create=true' + - '--reload-on-delete=true' + - '--reload-strategy=annotations' diff --git a/bin/arkade-install.sh b/bin/arkade-install.sh new file mode 100755 index 0000000..18a55b6 --- /dev/null +++ b/bin/arkade-install.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# TOOLS LIST +ARKADE_APPS="kubectl vcluster" + +# Add the arkade binary directory to the path if missing +case ":${PATH}:" in + *:"${HOME}/.arkade/bin":*) ;; + *) export PATH="${PATH}:${HOME}/.arkade/bin" ;; +esac + +# Install or update arkade +if command -v arkade >/dev/null; then + echo "Trying to update the arkade application" + sudo arkade update +else + echo "Installing the arkade application" + curl -sLS https://get.arkade.dev | sudo sh +fi + +echo "" +echo "Installing tools with arkade" +echo "" +for app in $ARKADE_APPS; do + app_path="$(command -v $app)" || true + if [ "$app_path" ]; then + echo "The application '$app' already available on '$app_path'" + else + arkade get "$app" + fi +done + +cat < Creating or updating the vcluster" +vcluster create my-vcluster --namespace my-vcluster --upgrade --connect=false \ + --values vcluster.yaml + +echo "=> Adding a traefik ingress route tcp to the vcluster api" +kubectl apply -f ingress_route_tcp.yaml + +echo "=> Exporting vcluster kubeconfig" +tries="0" +while true; do + if kubectl get -n my-vcluster secret/my-vcluster-kubeconfig >/dev/null 2>&1; then + # Dump the kubeconfig if available + kubectl get -n my-vcluster secret/my-vcluster-kubeconfig \ + --template="{{.data.config}}" | base64 -d > ~/.kube/my-vcluster-config + echo "KubeConfig dumped to '~/.kube/my-vcluster-config'" + break + fi + tries="$((tries + 1))" + echo "KubeConfig not avaliable after attempt '$tries'" + if [ "$tries" -lt "10" ]; then + sleep 5 + else + break + fi +done + +if [ "$tries" -eq "10" ]; then + echo "=> Failed to get the vcluster kubeconfig, try again" + exit 1 +fi + +cat < NOTE +Overwrite the KUBECONFIG variable to access the vcluster with 'kubectl': + + KUBECONFIG=~/.kube/my-vcluster-config kubectl + +You can define the following 'vkubectl' alias to make it easier: + + alias vkubectl="KUBECONFIG=~/.kube/my-vcluster-config kubectl" + +Or you can merge it with the one on the KUBECONFIG variable and use 'kubectx' or +a similar tool to change the context (for our vcluster the context will be +'my-vcluster_k3d-argocd'). + +If your KUBECONFIG variable is defined and only has the path to a single file +you can merge it with the vcluster config running the following: + + KUBECONFIG="\$KUBECONFIG:~/.kube/my-vcluster-config" \\ + kubectl config view --flatten > "\$KUBECONFIG.new"; + mv "\$KUBECONFIG.new" "\$KUBECONFIG" +EOF diff --git a/bin/install-vcluster-apps.sh b/bin/install-vcluster-apps.sh new file mode 100755 index 0000000..2179486 --- /dev/null +++ b/bin/install-vcluster-apps.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +set -e + +# Applications to install +APPS="dummyhttp reloader" + +# Relative PATH to the workdir from the script directory +WORK_DIR_RELPATH="../apps" + +# Compute WORKDIR +SCRIPT="$(readlink -f "$0")" +SCRIPT_DIR="$(dirname "$SCRIPT")" +WORK_DIR="$(readlink -f "$SCRIPT_DIR/$WORK_DIR_RELPATH")" + +# Update the PATH to add the arkade bin directory +# Add the arkade binary directory to the path if missing +case ":${PATH}:" in + *:"${HOME}/.arkade/bin":*) ;; + *) export PATH="${PATH}:${HOME}/.arkade/bin" ;; +esac + +# Go to the working directory +cd "$WORK_DIR" || exit 1 + +for app in $APPS; do + echo "=> Deploying the '$app' application" + kustomize build "$app" | \ + KUBECONFIG=~/.kube/my-vcluster-config kubectl apply -f - +done diff --git a/bin/vcluster-dummyhttp-secret.sh b/bin/vcluster-dummyhttp-secret.sh new file mode 100755 index 0000000..ba73dfc --- /dev/null +++ b/bin/vcluster-dummyhttp-secret.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e + +# VARIABLES +SECRET_VALUE="${*:-Vcluster Boo}" +SECRET_CONTEXT="k3d-argocd" +SECRET_NAME="dummyhttp-secret" +SECRET_NAMESPACE="my-vcluster-default" + +# Relative PATH to the workdir from the script directory +WORK_DIR_RELPATH="../" + +# Compute WORKDIR +SCRIPT="$(readlink -f "$0")" +SCRIPT_DIR="$(dirname "$SCRIPT")" +WORK_DIR="$(readlink -f "$SCRIPT_DIR/$WORK_DIR_RELPATH")" + +# Update the PATH to add the arkade bin directory +# Add the arkade binary directory to the path if missing +case ":${PATH}:" in + *:"${HOME}/.arkade/bin":*) ;; + *) export PATH="${PATH}:${HOME}/.arkade/bin" ;; +esac + +# Go to the working directory +cd "$WORK_DIR" || exit 1 + +echo "=> Checking if the '$SECRET_NAMESPACE' is available" +if kubectl get namespace "$SECRET_NAMESPACE" >/dev/null 2>&1; then + echo "The '$SECRET_NAMESPACE' namespace already exists" +else + kubectl create namespace "$SECRET_NAMESPACE" +fi + +echo "=> Creating secret file with SECRET_VAR = '$SECRET_VALUE'" +echo -n "$SECRET_VALUE" | \ + kubectl create secret generic "$SECRET_NAME" --namespace "$SECRET_NAMESPACE" \ + --dry-run=client --from-file=SECRET_VAR=/dev/stdin -o yaml \ + >dummyhttp-secret.yaml + +echo "=> Creating sealed secret on file" +kubeseal --context "$SECRET_CONTEXT" -f dummyhttp-secret.yaml \ + -w dummyhttp-sealed-secret.yaml + +rm -f dummyhttp-secret.yaml + +echo "=> Adding or updating sealed secret on '$SECRET_NAMESPACE' namespace" +kubectl apply --context "$SECRET_CONTEXT" -f dummyhttp-sealed-secret.yaml + +rm -f dummyhttp-sealed-secret.yaml + +tries="0" +while true; do + if kubectl get --context "$SECRET_CONTEXT" --namespace "$SECRET_NAMESPACE" \ + secret/dummyhttp-secret >/dev/null 2>&1; then + break + fi + tries="$((tries+1))" + echo "Secret not avaliable after attempt '$tries'" + if [ "$tries" -lt "10" ]; then + sleep 2 + else + break + fi +done + +if [ "$tries" -eq "10" ]; then + echo "Failed to get 'dummyhttp-secret', something dind't work as expected" + exit 1 +fi + +SECRET_VAR="$( + kubectl get --context "$SECRET_CONTEXT" --namespace "$SECRET_NAMESPACE" \ + secret/dummyhttp-secret --template="{{.data.SECRET_VAR}}" | base64 -d +)" + +echo "=> Found secret/dummyhttp-secret: SECRET_VAR = '$SECRET_VAR'" diff --git a/vcluster/ingress_route_tcp.yaml b/vcluster/ingress_route_tcp.yaml new file mode 100644 index 0000000..736c167 --- /dev/null +++ b/vcluster/ingress_route_tcp.yaml @@ -0,0 +1,15 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRouteTCP +metadata: + name: my-vcluster-api + namespace: my-vcluster +spec: + entryPoints: + - websecure + routes: + - match: HostSNI(`my-vcluster-api.lo.mixinet.net`) + services: + - name: my-vcluster + port: 443 + tls: + passthrough: true diff --git a/vcluster/vcluster.yaml b/vcluster/vcluster.yaml new file mode 100644 index 0000000..ce2664b --- /dev/null +++ b/vcluster/vcluster.yaml @@ -0,0 +1,31 @@ +controlPlane: + proxy: + # Extra hostnames to sign the vCluster proxy certificate for + extraSANs: + - my-vcluster-api.lo.mixinet.net +exportKubeConfig: + context: my-vcluster_k3d-argocd + server: https://my-vcluster-api.lo.mixinet.net:8443 + secret: + name: my-vcluster-kubeconfig +sync: + toHost: + ingresses: + enabled: true + serviceAccounts: + enabled: true + fromHost: + ingressClasses: + enabled: true + nodes: + enabled: true + clearImageStatus: true + secrets: + enabled: true + mappings: + byName: + # Sync all Secrets from the 'my-vcluster-default' namespace to the + # virtual 'default' namespace. + "my-vcluster-default/*": "default/*" + # We could add other namespace mappings if needed, i.e.: + # "my-vcluster-kube-system/*": "kube-system/*"