#!/usr/bin/env bash set -ueo pipefail SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" if [[ -n "${DEBUG:-}" ]]; then set -x; fi # Redirect all stdout to stderr. { if ! hcloud version >/dev/null; then echo "ERROR: 'hcloud' CLI not found, please install it and make it available on your \$PATH"; exit 1; fi if ! k3sup version >/dev/null; then echo "ERROR: 'k3sup' not found, please install it and make it available on your \$PATH"; exit 1; fi if ! helm version >/dev/null; then echo "ERROR: 'helm' not found, please install it and make it available on your \$PATH"; exit 1; fi if [[ "${HCLOUD_TOKEN:-}" == "" ]]; then echo "ERROR: please set \$HCLOUD_TOKEN"; exit 1; fi # We run a lot of subshells below for speed. If any encounter an error, we shut down the whole process group, pronto. function error() { echo "Onoes, something went wrong! :( The output above might have some clues." kill 0 } trap error ERR image_name=${IMAGE_NAME:-ubuntu-20.04} instance_count=${INSTANCES:-3} instance_type=${INSTANCE_TYPE:-cpx11} location=${LOCATION:-fsn1} network_zone=${NETWORK_ZONE:-eu-central} ssh_keys=${SSH_KEYS:-} channel=${K3S_CHANNEL:-stable} network_cidr=${NETWORK_CIDR:-10.0.0.0/8} subnet_cidr=${SUBNET_CIDR:-10.0.0.0/24} cluster_cidr=${CLUSTER_CIDR:-10.244.0.0/16} scope="${SCOPE:-dev}" scope=${scope//[^a-zA-Z0-9_]/-} scope_name=csi-${scope} label="managedby=hack,scope=$scope_name" ssh_private_key="$SCRIPT_DIR/.ssh-$scope" k3s_opts=${K3S_OPTS:-"--kubelet-arg cloud-provider=external"} k3s_server_opts=${K3S_SERVER_OPTS:-"--disable-cloud-controller --disable=traefik --disable=servicelb --disable=local-storage --flannel-backend=none --cluster-cidr ${cluster_cidr}"} echo -n "$HCLOUD_TOKEN" > "$SCRIPT_DIR/.token-$scope" export KUBECONFIG="$SCRIPT_DIR/.kubeconfig-$scope" ssh_command="ssh -i $ssh_private_key -o StrictHostKeyChecking=off -o BatchMode=yes -o ConnectTimeout=5" # Generate SSH keys and upload publkey to Hetzner Cloud. ( trap error ERR [[ ! -f $ssh_private_key ]] && ssh-keygen -t ed25519 -f $ssh_private_key -C '' -N '' [[ ! -f $ssh_private_key.pub ]] && ssh-keygen -y -f $ssh_private_key > $ssh_private_key.pub if ! hcloud ssh-key describe $scope_name >/dev/null 2>&1; then hcloud ssh-key create --label $label --name $scope_name --public-key-from-file $ssh_private_key.pub fi ) & # Create Network ( trap error ERR if ! hcloud network describe $scope_name >/dev/null 2>&1; then hcloud network create --label $label --ip-range $network_cidr --name $scope_name hcloud network add-subnet --network-zone $network_zone --type cloud --ip-range $subnet_cidr $scope_name fi ) & for num in $(seq $instance_count); do # Create server and initialize Kubernetes on it with k3sup. ( trap error ERR server_name="$scope_name-$num" # Maybe cluster is already up and node is already there. if kubectl get node $server_name >/dev/null 2>&1; then exit 0 fi ip=$(hcloud server ip $server_name 2>/dev/null || true) if [[ -z "${ip:-}" ]]; then # Wait for SSH key until hcloud ssh-key describe $scope_name >/dev/null 2>&1; do sleep 1; done until hcloud network describe $scope_name >/dev/null 2>&1; do sleep 1; done createcmd="hcloud server create --image $image_name --label $label --location $location --name $server_name --ssh-key=$scope_name --type $instance_type --network $scope_name" for key in $ssh_keys; do createcmd+=" --ssh-key $key" done $createcmd ip=$(hcloud server ip $server_name) fi # Wait for SSH. until [ "$($ssh_command root@$ip echo ok 2>/dev/null)" = "ok" ]; do sleep 1 done $ssh_command root@$ip 'mkdir -p /etc/rancher/k3s && cat > /etc/rancher/k3s/registries.yaml' < $SCRIPT_DIR/k3s-registries.yaml private_ip=$(hcloud server describe $server_name -o format="{{ (index .PrivateNet 0).IP }}") k3s_node_ip_opts="--node-external-ip ${ip} --node-ip ${private_ip}" if [[ "$num" == "1" ]]; then # First node is control plane. k3sup install --print-config=false --ip $ip --k3s-channel $channel --k3s-extra-args "${k3s_server_opts} ${k3s_opts} ${k3s_node_ip_opts}" --local-path $KUBECONFIG --ssh-key $ssh_private_key else # All subsequent nodes are initialized as workers. # Can't go any further until control plane has bootstrapped a bit though. until $ssh_command root@$(hcloud server ip $scope_name-1 || true) stat /etc/rancher/node/password >/dev/null 2>&1; do sleep 1 done k3sup join --server-ip $(hcloud server ip $scope_name-1) --ip $ip --k3s-channel $channel --k3s-extra-args "${k3s_opts} ${k3s_node_ip_opts}" --ssh-key $ssh_private_key fi ) & # Wait for this node to show up in the cluster. ( trap error ERR; set +x until kubectl wait --for=condition=Ready node/$scope_name-$num >/dev/null 2>&1; do sleep 1; done echo $scope_name-$num is up and in cluster ) & done ( trap error ERR # Control plane init tasks. # This is running in parallel with the server init, above. # Wait for control plane to look alive. until kubectl get nodes >/dev/null 2>&1; do sleep 1; done; # Deploy private registry. ( trap error ERR if ! helm status -n kube-system registry >/dev/null 2>&1; then helm install registry docker-registry \ --repo=https://helm.twun.io \ -n kube-system \ --version 2.2.2 \ --set service.clusterIP=10.43.0.2 \ --set 'tolerations[0].key=node.cloudprovider.kubernetes.io/uninitialized' \ --set 'tolerations[0].operator=Exists' fi ) & # Install Cilium. ( trap error ERR if ! helm status -n kube-system cilium >/dev/null 2>&1; then helm install cilium cilium --repo https://helm.cilium.io/ -n kube-system --version 1.13.1 \ --set tunnel=disabled \ --set ipv4NativeRoutingCIDR=$cluster_cidr \ --set ipam.mode=kubernetes fi) & # Create HCLOUD_TOKEN Secret for hcloud-cloud-controller-manager. ( trap error ERR if ! kubectl -n kube-system get secret hcloud >/dev/null 2>&1; then kubectl -n kube-system create secret generic hcloud --from-literal="token=$HCLOUD_TOKEN" --from-literal="network=$scope_name" fi) & wait # Install hcloud-cloud-controller-manager ( trap error ERR if ! helm status -n kube-system hccm >/dev/null 2>&1; then helm install hccm hcloud-cloud-controller-manager --repo https://charts.hetzner.cloud/ -n kube-system --version 1.14.2 --set networking.enabled=true fi) & wait ) & wait echo "Success - cluster fully initialized and ready, why not see for yourself?" echo '$ kubectl get nodes' kubectl get nodes } >&2 echo "export KUBECONFIG=$KUBECONFIG" $SCRIPT_DIR/registry-port-forward.sh echo "export SKAFFOLD_DEFAULT_REPO=localhost:30666"