diff --git a/scripts/installation/all-in-one.sh b/scripts/installation/all-in-one.sh new file mode 100755 index 00000000..2cf3b10b --- /dev/null +++ b/scripts/installation/all-in-one.sh @@ -0,0 +1,494 @@ +#!/bin/bash + +# Copyright 2021 The KubeEdge Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script installs a all-in-one Sedna environment, including: +# - A Kubernetes v1.21 cluster with multi worker nodes, default none worker node. +# - KubeEdge with multi nodes, default is latest KubeEdge and one edge node. +# - Sedna, default is latest release version. +# +# It requires you: +# - 2 CPUs or more +# - 2GB+ free memory, depends on node number setting +# - 10GB+ free disk space +# - Internet connection(docker hub, github etc.) +# - Linux platform, such as ubuntu/centos +# - Docker 17.06+ +# +# Advanced options, influential env vars: +# +# NUM_CLOUD_WORKERS | optional | The cloud workers, default 0 +# NUM_EDGE_WORKERS | optional | The KubeEdge workers, default 1 +# KUBEEDGE_VERSION | optional | The KubeEdge version to be installed. +# if not specified, it will get latest release version. +# CLUSTER_NAME | optional | The all-in-one cluster name, default 'sedna-mini' +# FORCE_INSTALL_SEDNA | optional | If 'true', force reinstall Sedna, default false. +# NODE_IMAGE | optional | Custom node image +# REUSE_EDGE_CONTAINER | optional | Whether reuse edge node containers or not, default is true + +set -o errexit +set -o nounset +set -o pipefail + + +function prepare_env() { + : ${CLUSTER_NAME:=sedna-mini} + + # here not use := because it ignore the error of get_latest_version command + if [ -z "${KUBEEDGE_VERSION:-}" ]; then + KUBEEDGE_VERSION=$(get_latest_version kubeedge/kubeedge v1.8.0) + fi + + : ${NUM_CLOUD_WORKERS:=0} + : ${NUM_EDGE_WORKERS:=1} + + # just reuse kind base image + # https://github.com/kubernetes-sigs/kind/blob/4910c3e221a858e68e29f9494170a38e1c4e8b80/pkg/build/nodeimage/defaults.go#L23 + + : ${ALLINONE_NODE_IMAGE:=kubeedge/sedna-allinone-node:v1.21.1} + + readonly MAX_CLOUD_WORKERS=2 + readonly MAX_EDGE_WORKERS=3 + + # TODO: find a better way to figure this kind control plane + readonly CONTROL_PLANE_NAME=${CLUSTER_NAME}-control-plane + + # cloudcore default websocket port + : ${CLOUDCORE_WS_PORT:=10000} + # cloudcore default cert port + : ${CLOUDCORE_CERT_PORT:=10002} + + # for debug purpose + : ${RETAIN_CONTAINER:=} + + # use existing edge node containers + # default is true + : ${REUSE_EDGE_CONTAINER:=true} + + # force install sedna control plane + # default is false + : ${FORCE_INSTALL_SEDNA:=false} + + # The docker network for edge nodes to separate the network of control plane. + # Since `kind` CNI doesn't support edge node, here just use the network 'kind'. + # TODO(llhuii): find a way to use the default docker network 'bridge'. + : ${EDGE_NODE_NETWORK:=kind} + + + validate_env +} + +function validate_env() { + + ((NUM_CLOUD_WORKERS<=MAX_CLOUD_WORKERS)) || { + log_fault "Only support NUM_CLOUD_WORKERS at most $MAX_CLOUD_WORKERS" + } + + ((NUM_EDGE_WORKERS<=MAX_EDGE_WORKERS)) || { + log_fault "Only support NUM_EDGE_WORKERS at most $MAX_EDGE_WORKERS" + } +} + + +function _log() { + local level=$1 + shift + timestamp=$(date +"[$level%m%d %H:%M:%S.%3N]") + echo "$timestamp $@" +} + +function log_fault() { + _log E "$@" >&2 + exit 2 +} + +function log_error() { + _log E "$@" >&2 +} + +function log_info() { + _log I "$@" +} + +function gen_kind_config() { + cat </dev/null || keadm init --kubeedge-version=$version --advertise-address=$CLOUDCORE_ADVERTISE_ADDRESSES"' + + # wait token to be created + exit_code=1 + TIMEOUT=30 # in seconds + for((i=1;i<=TIMEOUT; i++)); do + keadm gettoken >/dev/null 2>&1 && exit_code=0 && break + echo -ne "Waiting cloudcore to generate token, $i seconds...\r" + sleep 1 + done + echo + if [ $exit_code -gt 0 ]; then + log_lines=50 + tail -$log_lines /var/log/kubeedge/cloudcore.log | sed "s/^/ /" + echo "Timeout to wait cloudcore, above are the last $log_lines log of cloudcore." + fi + exit $exit_code + ' + KUBEEDGE_TOKEN=$(run_in_control_plane keadm gettoken) +} + +function gen_cni_config() { + cat </dev/null || { + keadm join \ + --cloudcore-ipport=${CLOUDCORE_EXPOSED_ADDR} \ + --certport=${CLOUDCORE_EXPOSED_CERT_PORT} \ + --token=$KUBEEDGE_TOKEN \ + --kubeedge-version '$version' \ + --edgenode-name '$hostname' \ + --remote-runtime-endpoint unix:///var/run/containerd/containerd.sock \ + --runtimetype remote + + # set imageGCHighThreshold to 100% for no image gc + sed -i 's/imageGCHighThreshold:.*/imageGCHighThreshold: 100/' /etc/kubeedge/config/edgecore.yaml && + systemctl restart edgecore || + true # ignore the error + } + + " + gen_cni_config | docker exec -i $containername tee /etc/cni/net.d/10-edgecni.conflist >/dev/null + + done +} + +function clean_edgenodes() { + for cid in $(docker ps -a --filter label=sedna.io=sedna-mini-edge -q); do + docker stop $cid; docker rm $cid + done +} + +function get_docker_network_gw() { + docker network inspect ${1-bridge} --format='{{(index .IPAM.Config 0).Gateway}}' +} + +function setup_cloud() { + create_k8s_cluster + + patch_kindnet + + setup_control_kubeconfig + + setup_cloudcore +} + +function clean_cloud() { + clean_k8s_cluster +} + +function setup_edge() { + create_and_setup_edgenodes +} + +function clean_edge() { + clean_edgenodes +} + +function install_sedna() { + local gm_node=$CONTROL_PLANE_NAME + if run_in_control_plane kubectl get ns sedna; then + if [ "$FORCE_INSTALL_SEDNA" != true ]; then + log_info '"sedna" namespace already exists, no install Sedna control components.' + log_info 'If want to reinstall them, you can remove it `kubectl delete ns sedna` or set FORCE_INSTALL_SEDNA=true!' + log_info + return + fi + run_in_control_plane bash -ec " + curl https://raw.githubusercontent.com/kubeedge/sedna/main/scripts/installation/install.sh | SEDNA_GM_NODE=$gm_node SEDNA_ACTION=clean bash - + " + fi + + log_info "Installing Sedna Control Components..." + + run_in_control_plane bash -ec " + curl https://raw.githubusercontent.com/kubeedge/sedna/main/scripts/installation/install.sh | SEDNA_GM_NODE=$gm_node SEDNA_ACTION=create bash - + " +} + +function get_latest_version() { + # get the latest version of specified gh repo + local repo=${1} default_version=${2} + # output of this latest page: + # ... + # "tag_name": "v1.0.0", + # ... + + # Sometimes this will reach rate limit + # https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting + local url=https://api.github.com/repos/$repo/releases/latest + if ! curl --fail -s $url | awk '/"tag_name":/&&$0=$2' | sed 's/[",]//g'; then + log_error "Error to get latest version of $repo: $(curl -s $url | head)" + log_error "Fall back to default version: $default_version" + echo $default_version + fi +} + +function arch() { + local arch=$(uname -m) + case "$arch" in + x86_64) arch=amd64;; + *);; + esac + echo "$arch" +} + +function _download_tool() { + local name=$1 url=$2 + local file=/usr/local/bin/$name + curl -Lo $file $url + chmod +x $file +} + +function check_command_exists() { + type $1 >/dev/null 2>&1 +} + +function ensure_tool() { + local command=$1 download_url=$2 + if check_command_exists $command; then + return + fi + + _download_tool $command $download_url + +} + +function ensure_kind() { + local version=${KIND_VERSION:-0.11.1} + ensure_tool kind https://kind.sigs.k8s.io/dl/v${version/v}/kind-linux-$(arch) +} + +function ensure_kubectl() { + + local version=${KUBECTL_VERSION:-1.21.0} + ensure_tool kubectl https://dl.k8s.io/release/v${version/v}/bin/linux/$(arch)/kubectl +} + +function ensure_tools() { + ensure_kind + ensure_kubectl +} + +function main() { + ensure_tools + prepare_env + action=${1-create} + + case "$action" in + create) + # llh + setup_cloud + setup_edge + install_sedna + log_info "Mini Sedna is created successfully" + ;; + + delete|clean) + clean_edge + clean_cloud + log_info "Mini Sedna is uninstalled successfully" + ;; + + # As a source file, noop + __source__) + ;; + + *) + log_fault "Unknown action $action" + ;; + esac +} + +main "$@" diff --git a/scripts/installation/build-allinone-image.sh b/scripts/installation/build-allinone-image.sh new file mode 100755 index 00000000..e2da7fbe --- /dev/null +++ b/scripts/installation/build-allinone-image.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +# Copyright 2021 The KubeEdge Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script builds the node image for all-in-one Sedna. + +set -o errexit +set -o nounset +set -o pipefail + + +# just reuse kind image +# https://github.com/kubernetes-sigs/kind/blob/4910c3e221a858e68e29f9494170a38e1c4e8b80/pkg/build/nodeimage/defaults.go#L23 +# +# Note: here use v1.21.1 of kindest/node, because kubeedge-1.8.0 still uses apiextensions.k8s.io/v1beta1 of CRD, which will be removed in k8s 1.22.0 +readonly BASE_IMAGE=kindest/node:v1.21.1 + +readonly BUILD_IMAGE_NAME=sedna-edge-build-$RANDOM + +function get_latest_version() { + # get the latest version of specified gh repo + local repo=${1} + # output of this latest page: + # ... + # "tag_name": "v1.0.0", + # ... + curl -s https://api.github.com/repos/$repo/releases/latest | awk '/"tag_name":/&&$0=$2' | sed 's/[",]//g' +} + +function create_build_container() { + docker run --rm --name $BUILD_IMAGE_NAME -d --entrypoint sleep $BASE_IMAGE inf || true + + if [ -z "$RETAIN_BUILD_CONTAINER" ]; then + trap clean_build_container EXIT + fi +} + +function clean_build_container() { + docker stop $BUILD_IMAGE_NAME +} + +function run_in_build_container() { + docker exec -i $BUILD_IMAGE_NAME "$@" +} + +function commit_build_container() { + local commit_args=( + docker commit + # put entrypoint back + # https://github.com/kubernetes-sigs/kind/blob/4910c3e221a858e68e29f9494170a38e1c4e8b80/images/base/Dockerfile#L203 + --change 'ENTRYPOINT [ "/usr/local/bin/entrypoint", "/sbin/init" ]' + $BUILD_IMAGE_NAME $ALLINONE_NODE_IMAGE + ) + "${commit_args[@]}" +} + +function gen_edgecore_config_template() { + run_in_build_container mkdir -p /etc/kubeedge + cat | run_in_build_container mkdir -p /etc/kubeedge +} + +function arch() { + local arch=$(uname -m) + case "$arch" in + x86_64) arch=amd64;; + *);; + esac + echo "$arch" +} + +function install_keadm() { + # download the specified keadm binary + local arch=$(arch) version=${KUBEEDGE_VERSION} + local tarfile=keadm-$version-linux-$arch.tar.gz + local path=keadm-$version-linux-$arch/keadm/keadm + local configdir=/etc/kubeedge + + run_in_build_container bash -euc " + # copy kube config file + # install keadm + curl --fail -LSO https://github.com/kubeedge/kubeedge/releases/download/$version/$tarfile + tar -xvf $tarfile $path + mv $path /usr/local/bin/ + rm $tarfile + + # install dependencies of keadm init/join + apt update -y + apt install -y wget sudo mosquitto + systemctl enable mosquitto + + # install debug tools + apt install -y less sqlite3 + + # add convenient command + echo 'alias docker=crictl' > ~/.bash_aliases + " + + # download the kubeedge into the docker image in advance + download_kubeedge +} + +function download_kubeedge() { + # download the specified kubeedge package for keadm + local arch=$(arch) version=${KUBEEDGE_VERSION} + local tarfile=kubeedge-$version-linux-$arch.tar.gz + local configdir=/etc/kubeedge + + run_in_build_container bash -euc " + mkdir -p $configdir; cd $configdir + curl --fail -LSO https://github.com/kubeedge/kubeedge/releases/download/$version/$tarfile + " +} + +function install_edgecore() { + # download the specified edgecore binary + local arch=$(arch) version=${KUBEEDGE_VERSION} + local tarfile=kubeedge-$version-linux-$arch.tar.gz + local edgecorepath=kubeedge-$version-linux-$arch/edge/edgecore + local configdir=/etc/kubeedge + + run_in_build_container bash -euc " + mkdir -p $configdir; cd $configdir + curl --fail -LSO https://github.com/kubeedge/kubeedge/releases/download/$version/$tarfile + + tar -xvf $tarfile $edgecorepath + mv $edgecorepath /usr/local/bin/ + rm $tarfile + + # install service + curl --fail -LSO https://raw.githubusercontent.com/kubeedge/kubeedge/$version/build/tools/edgecore.service + systemctl enable $configdir/edgecore.service + " +} + +: ${KUBEEDGE_VERSION:=$(get_latest_version kubeedge/kubeedge)} + +: ${NODE_IMAGE:=kubeedge/sedna-allinone-node:v1.21.1} +: ${RETAIN_BUILD_CONTAINER:=} + +create_build_container +install_keadm +commit_build_container +