istio.io/istio@v0.0.0-20240520182934-d79c90f27776/samples/kind-lb/setupkind.sh (about)

     1  #!/bin/bash
     2  # Copyright Istio Authors
     3  #
     4  #   Licensed under the Apache License, Version 2.0 (the "License");
     5  #
     6  set -e
     7  
     8  # This script can only produce desired results on Linux systems.
     9  ENVOS=$(uname 2>/dev/null || true)
    10  if [[ "${ENVOS}" != "Linux" ]]; then
    11    echo "Your system is not supported by this script. Only Linux is supported"
    12    exit 1
    13  fi
    14  
    15  # Check prerequisites
    16  REQUISITES=("kubectl" "kind" "docker")
    17  for item in "${REQUISITES[@]}"; do
    18    if [[ -z $(which "${item}") ]]; then
    19      echo "${item} cannot be found on your system, please install ${item}"
    20      exit 1
    21    fi
    22  done
    23  
    24  # Function to print the usage message
    25  function printHelp() {
    26    echo "Usage: "
    27    echo "    $0 --cluster-name cluster1 --k8s-release 1.22.1 --ip-space 255"
    28    echo ""
    29    echo "Where:"
    30    echo "    -n|--cluster-name  - name of the k8s cluster to be created"
    31    echo "    -r|--k8s-release   - the release of the k8s to setup, latest available if not given"
    32    echo "    -s|--ip-space      - the 2nd to the last part for public ip addresses, 255 if not given, valid range: 0-255."
    33    echo "    -m|--mode          - setup the required number of nodes per deployment model. Values are sidecar (1 node) or ambient (minimum of 2)"
    34    echo "    -w|--worker-nodes  - the number of worker nodes to create. Default is 1"
    35    echo "    --pod-subnet       - the pod subnet to specify. Default is 10.244.0.0/16 for IPv4 and fd00:10:244::/56 for IPv6"
    36    echo "    --service-subnet   - the service subnet to specify. Default is 10.96.0.0/16 for IPv4 and fd00:10:96::/112 for IPv6"
    37    echo "    -i|--ip-family     - ip family to be supported, default is ipv4 only. Value should be ipv4, ipv6, or dual"
    38    echo "    --ipv6gw           - set ipv6 as the gateway, necessary for dual-stack IPv6-preferred clusters"
    39    echo "    -h|--help          - print the usage of this script"
    40  }
    41  
    42  # Setup default values
    43  CLUSTERNAME="cluster1"
    44  K8SRELEASE=""
    45  IPSPACE=255
    46  IPFAMILY="ipv4"
    47  MODE="sidecar"
    48  NUMNODES=""
    49  PODSUBNET=""
    50  SERVICESUBNET=""
    51  IPV6GW=false
    52  
    53  # Handling parameters
    54  while [[ $# -gt 0 ]]; do
    55    optkey="$1"
    56    case $optkey in
    57      -n|--cluster-name)
    58        CLUSTERNAME="$2"; shift 2;;
    59      -r|--k8s-release)
    60        K8SRELEASE="--image=kindest/node:v$2"; shift 2;;
    61      -s|--ip-space)
    62        IPSPACE="$2"; shift 2;;
    63      -m|--mode)
    64        MODE="$2"; shift 2;;
    65      -w|--worker-nodes)
    66        NUMNODES="$2"; shift 2;;
    67      --pod-subnet)
    68        PODSUBNET="$2"; shift 2;;
    69      --service-subnet)
    70        SERVICESUBNET="$2"; shift 2;;
    71      -i|--ip-family)
    72        IPFAMILY="${2,,}";shift 2;;
    73      --ipv6gw)
    74        IPV6GW=true; shift;;
    75      -h|--help)
    76        printHelp; exit 0;;
    77      *) # unknown option
    78        echo "parameter $1 is not supported"; printHelp; exit 1;;
    79    esac
    80  done
    81  
    82  # This block is to setup kind to have a local image repo to push
    83  # images using localhost:5000, to use this feature, start up
    84  # a registry container such as gcr.io/istio-testing/registry, then
    85  # connect it to the docker network where kind nodes are running on
    86  # which normally will be called kind
    87  FEATURES=$(cat << EOF
    88  featureGates:
    89    MixedProtocolLBService: true
    90    GRPCContainerProbe: true
    91  kubeadmConfigPatches:
    92    - |
    93      apiVersion: kubeadm.k8s.io/v1beta2
    94      kind: ClusterConfiguration
    95      metadata:
    96        name: config
    97      etcd:
    98        local:
    99          # Run etcd in a tmpfs (in RAM) for performance improvements
   100          dataDir: /tmp/kind-cluster-etcd
   101      # We run single node, drop leader election to reduce overhead
   102      controllerManagerExtraArgs:
   103        leader-elect: "false"
   104      schedulerExtraArgs:
   105        leader-elect: "false"
   106      apiServer:
   107        extraArgs:
   108          "service-account-issuer": "kubernetes.default.svc"
   109          "service-account-signing-key-file": "/etc/kubernetes/pki/sa.key"
   110  containerdConfigPatches:
   111    - |-
   112      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"]
   113        endpoint = ["http://kind-registry:5000"]
   114  EOF
   115  )
   116  
   117  validIPFamilies=("ipv4" "ipv6" "dual")
   118  # Validate if the ip family value is correct.
   119  isValid="false"
   120  for family in "${validIPFamilies[@]}"; do
   121    if [[ "$family" == "${IPFAMILY}" ]]; then
   122      isValid="true"
   123      break
   124    fi
   125  done
   126  
   127  if [[ "${isValid}" == "false" ]]; then
   128    echo "${IPFAMILY} is not valid ip family, valid values are ipv4, ipv6 or dual"
   129    exit 1
   130  fi
   131  
   132  if [[ "${MODE}" == "ambient" ]]; then
   133    NUMNODES=${NUMNODES:-2}
   134  fi
   135  
   136  NODES=$(cat <<-EOM
   137  nodes:
   138  - role: control-plane
   139  EOM
   140  )
   141  
   142  if [[ -n "${NUMNODES}" ]]; then
   143  for _ in $(seq 1 "${NUMNODES}"); do
   144    NODES+=$(printf "\n%s" "- role: worker")
   145  done
   146  fi
   147  
   148  CONFIG=$(cat <<-EOM
   149  kind: Cluster
   150  apiVersion: kind.x-k8s.io/v1alpha4
   151  ${FEATURES}
   152  name: ${CLUSTERNAME}
   153  ${NODES}
   154  networking:
   155    ipFamily: ${IPFAMILY}
   156  EOM
   157  )
   158  
   159  if [[ -n "${PODSUBNET}" ]]; then
   160    CONFIG+=$(printf "\n%s" "  podSubnet: \"${PODSUBNET}\"")
   161  fi
   162  
   163  if [[ -n "${SERVICESUBNET}" ]]; then
   164    CONFIG+=$(printf "\n%s" "  serviceSubnet: \"${SERVICESUBNET}\"")
   165  fi
   166  
   167  # Create k8s cluster using the giving release and name
   168  if [[ -z "${K8SRELEASE}" ]]; then
   169    cat << EOF | kind create cluster --config -
   170  ${CONFIG}
   171  EOF
   172  else
   173    cat << EOF | kind create cluster "${K8SRELEASE}" --config -
   174  ${CONFIG}
   175  EOF
   176  fi
   177  
   178  # Setup cluster context
   179  kubectl cluster-info --context "kind-${CLUSTERNAME}"
   180  
   181  # Setup metallb using v0.13.11
   182  kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.11/config/manifests/metallb-native.yaml
   183  
   184  addrName="IPAddress"
   185  ipv4Prefix=""
   186  ipv6Prefix=""
   187  
   188  # Get both ipv4 and ipv6 gateway for the cluster
   189  gatewaystr=$(docker network inspect -f '{{range .IPAM.Config }}{{ .Gateway }} {{end}}' kind | cut -f1,2)
   190  read -r -a gateways <<< "${gatewaystr}"
   191  for gateway in "${gateways[@]}"; do
   192    if [[ "$gateway" == *"."* ]]; then
   193      ipv4Prefix=$(echo "${gateway}" |cut -d'.' -f1,2)
   194    else
   195      ipv6Prefix=$(echo "${gateway}" |cut -d':' -f1,2,3,4)
   196    fi
   197  done
   198  
   199  if [[ "${IPFAMILY}" == "ipv4" ]]; then
   200    addrName="IPAddress"
   201    ipv4Range="- ${ipv4Prefix}.${IPSPACE}.200-${ipv4Prefix}.${IPSPACE}.240"
   202    ipv6Range=""
   203  elif [[ "${IPFAMILY}" == "ipv6" ]]; then
   204    addrName="GlobalIPv6Address"
   205    ipv4Range=""
   206    ipv6Range="- ${ipv6Prefix}::${IPSPACE}:200-${ipv6Prefix}::${IPSPACE}:240"
   207  else
   208    if [[ "${IPV6GW}" == "true" ]]; then
   209      addrName="GlobalIPv6Address"
   210    fi
   211  
   212    ipv4Range="- ${ipv4Prefix}.${IPSPACE}.200-${ipv4Prefix}.${IPSPACE}.240"
   213    ipv6Range="- ${ipv6Prefix}::${IPSPACE}:200-${ipv6Prefix}::${IPSPACE}:240"
   214  fi
   215  
   216  # utility function to wait for pods to be ready
   217  function waitForPods() {
   218    ns=$1
   219    lb=$2
   220    waittime=$3
   221    # Wait for the pods to be ready in the given namespace with lable
   222    while : ; do
   223      res=$(kubectl wait --context "kind-${CLUSTERNAME}" -n "${ns}" pod \
   224        -l "${lb}" --for=condition=Ready --timeout="${waittime}s" 2>/dev/null ||true)
   225      if [[ "${res}" == *"condition met"* ]]; then
   226        break
   227      fi
   228      echo "Waiting for pods in namespace ${ns} with label ${lb} to be ready..."
   229      sleep "${waittime}"
   230    done
   231  }
   232  
   233  waitForPods metallb-system app=metallb 10
   234  
   235  # Now configure the loadbalancer public IP range
   236  cat <<EOF | kubectl apply -f -
   237  apiVersion: metallb.io/v1beta1
   238  kind: IPAddressPool
   239  metadata:
   240    namespace: metallb-system
   241    name: address-pool
   242  spec:
   243    addresses:
   244      ${ipv4Range}
   245      ${ipv6Range}
   246  ---
   247  apiVersion: metallb.io/v1beta1
   248  kind: L2Advertisement
   249  metadata:
   250    name: empty
   251    namespace: metallb-system
   252  EOF
   253  
   254  # Wait for the public IP address to become available.
   255  while : ; do
   256    ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.'${addrName}'}}{{end}}' "${CLUSTERNAME}"-control-plane)
   257    if [[ -n "${ip}" ]]; then
   258      #Change the kubeconfig file not to use the loopback IP
   259      if [[ "${IPFAMILY}" == "ipv6" ]]; then
   260        ip="[${ip}]"
   261      elif [[ "${IPFAMILY}" == "dual" ]] && [[ "${IPV6GW}" == "true" ]]; then
   262        ip="[${ip}]"
   263      fi
   264      kubectl config set clusters.kind-"${CLUSTERNAME}".server https://"${ip}":6443
   265      break
   266    fi
   267    echo 'Waiting for public IP address to be available...'
   268    sleep 3
   269  done
   270  
   271  echo "Kubernetes cluster ${CLUSTERNAME} was created successfully!"