272 lines
7.9 KiB
Bash
272 lines
7.9 KiB
Bash
#!/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 <<EOF | kubectl apply -f -
|
|
apiVersion: metallb.io/v1beta1
|
|
kind: IPAddressPool
|
|
metadata:
|
|
namespace: metallb-system
|
|
name: address-pool
|
|
spec:
|
|
addresses:
|
|
${ipv4Range}
|
|
${ipv6Range}
|
|
---
|
|
apiVersion: metallb.io/v1beta1
|
|
kind: L2Advertisement
|
|
metadata:
|
|
name: empty
|
|
namespace: metallb-system
|
|
EOF
|
|
|
|
# Wait for the public IP address to become available.
|
|
while : ; do
|
|
ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.'${addrName}'}}{{end}}' "${CLUSTERNAME}"-control-plane)
|
|
if [[ -n "${ip}" ]]; then
|
|
#Change the kubeconfig file not to use the loopback IP
|
|
if [[ "${IPFAMILY}" == "ipv6" ]]; then
|
|
ip="[${ip}]"
|
|
elif [[ "${IPFAMILY}" == "dual" ]] && [[ "${IPV6GW}" == "true" ]]; then
|
|
ip="[${ip}]"
|
|
fi
|
|
kubectl config set clusters.kind-"${CLUSTERNAME}".server https://"${ip}":6443
|
|
break
|
|
fi
|
|
echo 'Waiting for public IP address to be available...'
|
|
sleep 3
|
|
done
|
|
|
|
echo "Kubernetes cluster ${CLUSTERNAME} was created successfully!"
|