#!/bin/bash # Copyright Istio Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # set -e # This script can only produce desired results on Linux systems. ENVOS=$(uname 2>/dev/null || true) if [[ "${ENVOS}" != "Linux" ]]; then echo "Your system is not supported by this script. Only Linux is supported" exit 1 fi # Check prerequisites REQUISITES=("kubectl" "kind" "docker") for item in "${REQUISITES[@]}"; do if [[ -z $(which "${item}") ]]; then echo "${item} cannot be found on your system, please install ${item}" exit 1 fi done # Function to print the usage message function printHelp() { echo "Usage: " echo " $0 --cluster-name cluster1 --k8s-release 1.22.1 --ip-space 255" echo "" echo "Where:" echo " -n|--cluster-name - name of the k8s cluster to be created" echo " -r|--k8s-release - the release of the k8s to setup, latest available if not given" echo " -s|--ip-space - the 2nd to the last part for public ip addresses, 255 if not given, valid range: 0-255." echo " -m|--mode - setup the required number of nodes per deployment model. Values are sidecar (1 node) or ambient (minimum of 2)" echo " -w|--worker-nodes - the number of worker nodes to create. Default is 1" echo " --pod-subnet - the pod subnet to specify. Default is 10.244.0.0/16 for IPv4 and fd00:10:244::/56 for IPv6" echo " --service-subnet - the service subnet to specify. Default is 10.96.0.0/16 for IPv4 and fd00:10:96::/112 for IPv6" echo " -i|--ip-family - ip family to be supported, default is ipv4 only. Value should be ipv4, ipv6, or dual" echo " --ipv6gw - set ipv6 as the gateway, necessary for dual-stack IPv6-preferred clusters" echo " -h|--help - print the usage of this script" } # Setup default values CLUSTERNAME="cluster1" K8SRELEASE="" IPSPACE=255 IPFAMILY="ipv4" MODE="sidecar" NUMNODES="" PODSUBNET="" SERVICESUBNET="" IPV6GW=false # Handling parameters while [[ $# -gt 0 ]]; do optkey="$1" case $optkey in -n|--cluster-name) CLUSTERNAME="$2"; shift 2;; -r|--k8s-release) K8SRELEASE="--image=kindest/node:v$2"; shift 2;; -s|--ip-space) IPSPACE="$2"; shift 2;; -m|--mode) MODE="$2"; shift 2;; -w|--worker-nodes) NUMNODES="$2"; shift 2;; --pod-subnet) PODSUBNET="$2"; shift 2;; --service-subnet) SERVICESUBNET="$2"; shift 2;; -i|--ip-family) IPFAMILY="${2,,}";shift 2;; --ipv6gw) IPV6GW=true; shift;; -h|--help) printHelp; exit 0;; *) # unknown option echo "parameter $1 is not supported"; printHelp; exit 1;; esac done # This block is to setup kind to have a local image repo to push # images using localhost:5000, to use this feature, start up # a registry container such as gcr.io/istio-testing/registry, then # connect it to the docker network where kind nodes are running on # which normally will be called kind FEATURES=$(cat << EOF featureGates: MixedProtocolLBService: true GRPCContainerProbe: true kubeadmConfigPatches: - | apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration metadata: name: config etcd: local: # Run etcd in a tmpfs (in RAM) for performance improvements dataDir: /tmp/kind-cluster-etcd # We run single node, drop leader election to reduce overhead controllerManagerExtraArgs: leader-elect: "false" schedulerExtraArgs: leader-elect: "false" apiServer: extraArgs: "service-account-issuer": "kubernetes.default.svc" "service-account-signing-key-file": "/etc/kubernetes/pki/sa.key" containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"] endpoint = ["http://kind-registry:5000"] EOF ) validIPFamilies=("ipv4" "ipv6" "dual") # Validate if the ip family value is correct. isValid="false" for family in "${validIPFamilies[@]}"; do if [[ "$family" == "${IPFAMILY}" ]]; then isValid="true" break fi done if [[ "${isValid}" == "false" ]]; then echo "${IPFAMILY} is not valid ip family, valid values are ipv4, ipv6 or dual" exit 1 fi if [[ "${MODE}" == "ambient" ]]; then NUMNODES=${NUMNODES:-2} fi NODES=$(cat <<-EOM nodes: - role: control-plane EOM ) if [[ -n "${NUMNODES}" ]]; then for _ in $(seq 1 "${NUMNODES}"); do NODES+=$(printf "\n%s" "- role: worker") done fi CONFIG=$(cat <<-EOM kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 ${FEATURES} name: ${CLUSTERNAME} ${NODES} networking: ipFamily: ${IPFAMILY} EOM ) if [[ -n "${PODSUBNET}" ]]; then CONFIG+=$(printf "\n%s" " podSubnet: \"${PODSUBNET}\"") fi if [[ -n "${SERVICESUBNET}" ]]; then CONFIG+=$(printf "\n%s" " serviceSubnet: \"${SERVICESUBNET}\"") fi # Create k8s cluster using the giving release and name if [[ -z "${K8SRELEASE}" ]]; then cat << EOF | kind create cluster --config - ${CONFIG} EOF else cat << EOF | kind create cluster "${K8SRELEASE}" --config - ${CONFIG} EOF fi # Setup cluster context kubectl cluster-info --context "kind-${CLUSTERNAME}" # Setup metallb using v0.13.11 kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.11/config/manifests/metallb-native.yaml addrName="IPAddress" ipv4Prefix="" ipv6Prefix="" # Get both ipv4 and ipv6 gateway for the cluster gatewaystr=$(docker network inspect -f '{{range .IPAM.Config }}{{ .Gateway }} {{end}}' kind | cut -f1,2) read -r -a gateways <<< "${gatewaystr}" for gateway in "${gateways[@]}"; do if [[ "$gateway" == *"."* ]]; then ipv4Prefix=$(echo "${gateway}" |cut -d'.' -f1,2) else ipv6Prefix=$(echo "${gateway}" |cut -d':' -f1,2,3,4) fi done if [[ "${IPFAMILY}" == "ipv4" ]]; then addrName="IPAddress" ipv4Range="- ${ipv4Prefix}.${IPSPACE}.200-${ipv4Prefix}.${IPSPACE}.240" ipv6Range="" elif [[ "${IPFAMILY}" == "ipv6" ]]; then addrName="GlobalIPv6Address" ipv4Range="" ipv6Range="- ${ipv6Prefix}::${IPSPACE}:200-${ipv6Prefix}::${IPSPACE}:240" else if [[ "${IPV6GW}" == "true" ]]; then addrName="GlobalIPv6Address" fi ipv4Range="- ${ipv4Prefix}.${IPSPACE}.200-${ipv4Prefix}.${IPSPACE}.240" ipv6Range="- ${ipv6Prefix}::${IPSPACE}:200-${ipv6Prefix}::${IPSPACE}:240" fi # utility function to wait for pods to be ready function waitForPods() { ns=$1 lb=$2 waittime=$3 # Wait for the pods to be ready in the given namespace with lable while : ; do res=$(kubectl wait --context "kind-${CLUSTERNAME}" -n "${ns}" pod \ -l "${lb}" --for=condition=Ready --timeout="${waittime}s" 2>/dev/null ||true) if [[ "${res}" == *"condition met"* ]]; then break fi echo "Waiting for pods in namespace ${ns} with label ${lb} to be ready..." sleep "${waittime}" done } waitForPods metallb-system app=metallb 10 # Now configure the loadbalancer public IP range cat <