kubeasz/tools/easzctl

377 lines
14 KiB
Plaintext
Raw Normal View History

#!/bin/bash
#
2019-03-13 20:47:53 +08:00
# This script aims to manage k8s clusters created by 'kubeasz'. (developing)
set -o nounset
set -o errexit
#set -o xtrace
function usage() {
cat <<EOF
Usage: easzctl COMMAND [args]
2019-03-22 08:38:45 +08:00
Commands 1 (cluster-wide operation):
checkout To switch to cluster <clustername> context, or create it if not existed
2019-03-13 20:47:53 +08:00
destroy To destroy the current cluster, with '--purge' option to also delete the context
list To list all of clusters managed
setup To setup a cluster using the current context
2019-03-17 17:58:39 +08:00
start-aio To quickly setup an all-in-one cluster for testing (like minikube)
2019-03-22 08:38:45 +08:00
Commands 2 (in-cluster opration):
add-etcd To add a etcd-node to the etcd cluster
2019-03-26 08:56:52 +08:00
add-master To add a kube-master(master node) to the k8s cluster
add-node To add a kube-node(work node) to the k8s cluster
2019-03-22 08:38:45 +08:00
clean-node To clean a node, whatever role the node plays
2019-03-26 08:56:52 +08:00
del-etcd To delete a etcd-node from the etcd cluster
upgrade To upgrade the k8s cluster
Use "easzctl help <command>" for more information about a given command.
EOF
}
function help-info() {
case "$1" in
(add-node)
echo -e "Usage: easzctl add-node <new_node_ip>\n\nread 'https://github.com/gjmzj/kubeasz/blob/master/docs/op/AddNode.md'"
;;
(add-master)
echo -e "Usage: easzctl add-master <new_master_ip>\n\nread 'https://github.com/gjmzj/kubeasz/blob/master/docs/op/AddMaster.md'"
;;
(add-etcd)
echo -e "Usage: easzctl add-etcd <new_etcd_ip>\n\nread 'https://github.com/gjmzj/kubeasz/blob/master/docs/op/op-etcd.md'"
;;
(del-etcd)
echo -e "Usage: easzctl del-etcd <etcd_ip>\n\nread 'https://github.com/gjmzj/kubeasz/blob/master/docs/op/op-etcd.md'"
;;
(clean-node)
echo -e "Usage: easzctl clean-node <node_ip>\n\nread 'https://github.com/gjmzj/kubeasz/blob/master/docs/op/clean_one_node.md'"
;;
(*)
usage
return 0
;;
esac
}
function process_cmd() {
2019-03-17 17:58:39 +08:00
echo -e "[INFO] \033[33m$ACTION\033[0m : $CMD"
$CMD || { echo -e "[ERROR] \033[31mAction failed\033[0m : $CMD"; return 1; }
echo -e "[INFO] \033[32mAction successed\033[0m : $CMD"
}
### in-cluster operation functions ##############################
function add-node() {
# check new node's address regexp
2019-03-17 17:58:39 +08:00
[[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "[ERROR] Invalid ip address!"; return 1; }
# check if the new node already exsited
2019-03-17 17:58:39 +08:00
sed -n '/^\[kube-master/,/^\[harbor/p' $BASEPATH/hosts|grep "^$1" && { echo "[ERROR] node $1 already existed!"; return 2; }
# add a node into 'kube-node' group
sed -i "/\[kube-node/a $1 NEW_NODE=yes" $BASEPATH/hosts
# check if playbook runs successfully
ansible-playbook $BASEPATH/tools/20.addnode.yml -e NODE_TO_ADD=$1 || { sed -i "/$1 NEW_NODE=yes/d" $BASEPATH/hosts; return 2; }
# save current cluster context if needed
save_context
}
function add-master() {
# check new master's address regexp
2019-03-17 17:58:39 +08:00
[[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "[ERROR] Invalid ip address!"; return 2; }
# check if k8s with DPLOY_MODE='multi-master'
2019-03-17 17:58:39 +08:00
grep '^DEPLOY_MODE=multi-master' $BASEPATH/hosts || { echo "[ERROR] only k8s with DPLOY_MODE='multi-master' can have master node added!"; return 2; }
# check if the new master already exsited
2019-03-17 17:58:39 +08:00
sed -n '/^\[kube-master/,/^\[kube-node/p' $BASEPATH/hosts|grep "^$1" && { echo "[ERROR] master $1 already existed!"; return 2; }
# add a node into 'kube-master' group
sed -i "/\[kube-master/a $1 NEW_MASTER=yes" $BASEPATH/hosts
# check if playbook runs successfully
ansible-playbook $BASEPATH/tools/21.addmaster.yml -e NODE_TO_ADD=$1 || { sed -i "/$1 NEW_MASTER=yes/d" $BASEPATH/hosts; return 2; }
# save current cluster context if needed
save_context
}
function add-etcd() {
# check new node's address regexp
2019-03-17 17:58:39 +08:00
[[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "[ERROR] Invalid ip address!"; return 2; }
# check if the new node already exsited
2019-03-17 17:58:39 +08:00
sed -n '/^\[etcd/,/^\[kube-master/p' $BASEPATH/hosts|grep "^$1" && { echo "[ERROR] node $1 already existed!"; return 2; }
# input an unique NODE_NAME of the node in etcd cluster
echo "Please input an UNIQUE name(string) for the new node: "
2019-03-17 17:58:39 +08:00
read -t15 NAME
sed -n '/^\[etcd/,/^\[kube-master/p' $BASEPATH/hosts|grep "$NAME" && { echo "[ERROR] name [$NAME] already existed!"; return 2; }
# add a node into 'kube-node' group
sed -i "/\[etcd/a $1 NODE_NAME=$NAME" $BASEPATH/hosts
# check if playbook runs successfully
ansible-playbook $BASEPATH/tools/19.addetcd.yml -e NODE_TO_ADD=$1 || { sed -i "/$1 NODE_NAME=$NAME/d" $BASEPATH/hosts; return 2; }
# restart apiservers to use the new etcd cluster
ansible-playbook $BASEPATH/04.kube-master.yml -t restart_master || { echo "[ERROR] Unexpected failures in master nodes!"; return 2; }
# save current cluster context if needed
save_context
}
function del-etcd() {
# check node's address regexp
2019-03-17 17:58:39 +08:00
[[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "[ERROR] Invalid ip address!"; return 2; }
#
ansible-playbook $BASEPATH/tools/remove_etcd_node.yml -e ETCD_TO_DEL=$1
# restart apiservers to use the new etcd cluster
ansible-playbook $BASEPATH/04.kube-master.yml -t restart_master || { echo "[ERROR] Unexpected failures in master nodes!"; return 2; }
# save current cluster context if needed
save_context
}
function clean-node() {
# check node's address regexp
2019-03-17 17:58:39 +08:00
[[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "[ERROR] Invalid ip address!"; return 2; }
#
ansible-playbook $BASEPATH/tools/clean_one_node.yml -e NODE_TO_DEL=$1
# save current cluster context if needed
save_context
}
2019-03-26 08:56:52 +08:00
function upgrade() {
echo -e "[INFO] prepare the new binaries in advance"
echo -e "[INFO] upgrade begin in 5s, press any key to abort\n:"
! (read -t5 -n1 ANS) || { echo "[WARN] upgrade aborted"; return 1; }
ansible-playbook -t upgrade_k8s $BASEPATH/22.upgrade.yml || return 1
save_context
}
### cluster-wide operation functions ############################
function save_context() {
[ -f "$BASEPATH/.cluster/current_cluster" ] || { echo "[WARN] Invalid Context"; return 0; }
CLUSTER=$(cat $BASEPATH/.cluster/current_cluster)
echo "[INFO] save context: $CLUSTER"
echo "[INFO] save $CLUSTER roles' configration"
for ROLE in $(ls $BASEPATH/roles);
do
if [ -d "$BASEPATH/roles/$ROLE/defaults" ]; then
mkdir -p $BASEPATH/.cluster/$CLUSTER/roles/$ROLE/defaults/
cp -fpr $BASEPATH/roles/$ROLE/defaults/* $BASEPATH/.cluster/$CLUSTER/roles/$ROLE/defaults/
fi
done
2019-03-14 12:38:33 +08:00
if [ -f "$BASEPATH/hosts" ];then
2019-03-17 12:04:34 +08:00
echo "[INFO] save $CLUSTER ansible hosts"
cp -fp $BASEPATH/hosts $BASEPATH/.cluster/$CLUSTER/
2019-03-14 12:38:33 +08:00
fi
2019-03-14 12:38:33 +08:00
if [ -f /root/.kube/config ];then
2019-03-17 12:04:34 +08:00
echo "[INFO] save $CLUSTER kubeconfig"
cp -fp /root/.kube/config $BASEPATH/.cluster/$CLUSTER/
2019-03-14 12:38:33 +08:00
fi
}
function install_context() {
2019-03-17 17:58:39 +08:00
[ -f "$BASEPATH/.cluster/current_cluster" ] || { echo "[ERROR] Invalid Context"; return 1; }
CLUSTER=$(cat $BASEPATH/.cluster/current_cluster)
echo "[INFO] install context: $CLUSTER"
echo "[INFO] install $CLUSTER roles' configration"
for ROLE in $(ls $BASEPATH/.cluster/$CLUSTER/roles);
do
cp -fp $BASEPATH/.cluster/$CLUSTER/roles/$ROLE/defaults/* $BASEPATH/roles/$ROLE/defaults/
done
if [ -f "$BASEPATH/.cluster/$CLUSTER/hosts" ];then
2019-03-17 12:04:34 +08:00
echo "[INFO] install $CLUSTER ansible hosts"
cp -fp $BASEPATH/.cluster/$CLUSTER/hosts $BASEPATH/
2019-03-13 20:47:53 +08:00
fi
if [ -f "$BASEPATH/.cluster/$CLUSTER/config" ];then
2019-03-17 12:04:34 +08:00
echo "[INFO] install $CLUSTER kubeconfig"
cp -fp $BASEPATH/.cluster/$CLUSTER/config /root/.kube/
2019-03-13 20:47:53 +08:00
fi
}
function checkout() {
# check directory '.cluster', initialize it if not existed
if [ ! -d "$BASEPATH/.cluster" ]; then
echo "[INFO] initialize directory $BASEPATH/.cluster"
mkdir -p $BASEPATH/.cluster/default
echo default > $BASEPATH/.cluster/current_cluster
fi
# check if $1 is already the current context
CLUSTER=$(cat $BASEPATH/.cluster/current_cluster)
[ "$1" != "$CLUSTER" ] || { echo "[WARN] $1 is already the current context"; return 0; }
# save context of the current cluster
echo "[INFO] save current context: $CLUSTER"
save_context
echo "[INFO] clean context: $CLUSTER"
rm -rf $BASEPATH/hosts /root/.kube/*
# check context $1, install it if existed, otherwise initialize it using default context
if [ ! -d "$BASEPATH/.cluster/$1" ];then
echo "[INFO] context $1 not existed, initialize it using default context"
cp -rp $BASEPATH/.cluster/default $BASEPATH/.cluster/$1
rm -f $BASEPATH/.cluster/$1/hosts $BASEPATH/.cluster/$1/config
fi
echo "[INFO] change current context to $1"
echo $1 > $BASEPATH/.cluster/current_cluster
install_context;
}
function setup() {
2019-03-26 16:14:48 +08:00
[ -d "$BASEPATH/.cluster" ] || { echo "[ERROR] invalid context, run 'easzctl checkout <cluster_name>' first"; return 1; }
[ -f "$BASEPATH/bin/kube-apiserver" ] || { echo "[ERROR] no binaries found, download then fist"; return 1; }
[ -f "$BASEPATH/hosts" ] || { echo "[ERROR] no ansible hosts found, read 'docs/setup/00-planning_and_overall_intro.md'"; return 1; }
CLUSTER=$(cat $BASEPATH/.cluster/current_cluster)
echo -e "\n[INFO] setup cluster with context: $CLUSTER"
2019-03-26 08:56:52 +08:00
echo -e "[INFO] setup begin in 5s, press any key to abort\n:"
! (read -t5 -n1 ANS) || { echo "[WARN] setup aborted"; return 1; }
2019-03-17 19:19:57 +08:00
ansible-playbook $BASEPATH/90.setup.yml || return 1
2019-03-17 12:04:34 +08:00
save_context
}
function list() {
2019-03-14 12:38:33 +08:00
[ -d "$BASEPATH/.cluster" ] || { echo "[ERROR] invalid context, run 'easzctl checkout <cluster_name>' first"; return 1; }
CLUSTER=$(cat $BASEPATH/.cluster/current_cluster)
2019-03-17 12:04:34 +08:00
echo -e "\nlist of managed contexts (current: $CLUSTER)"
i=1; for Cluster in $(ls $BASEPATH/.cluster/ |grep -v current_cluster);
do
echo -e "==> context $i:\t$Cluster"
let "i++"
done
echo -e "\nlist of installed clusters (current: $CLUSTER)"
i=1; for Cluster in $(ls $BASEPATH/.cluster/ |grep -v current_cluster);
2019-03-13 20:47:53 +08:00
do
KUBECONF=$BASEPATH/.cluster/$Cluster/config
if [ -f "$KUBECONF" ]; then
2019-03-17 12:04:34 +08:00
echo -e "==> cluster $i:\t$Cluster"
2019-03-13 20:47:53 +08:00
$BASEPATH/bin/kubectl --kubeconfig=$KUBECONF get node
fi
let "i++"
done
}
2019-03-13 20:47:53 +08:00
function destroy() {
2019-03-14 12:38:33 +08:00
[ -d "$BASEPATH/.cluster" ] || { echo "[ERROR] invalid context, run 'easzctl checkout <cluster_name>' first"; return 1; }
2019-03-13 20:47:53 +08:00
CLUSTER=$(cat $BASEPATH/.cluster/current_cluster)
2019-03-17 12:04:34 +08:00
echo -n "[WARN] DELETE cluster: $CLUSTER, Continue? (y/n): "
read -t10 -n1 ANS || { echo -e "\n[WARN] timeout, destroy aborted"; return 1; }
2019-03-13 20:47:53 +08:00
if [[ -n $ANS && $ANS == y ]];then
2019-03-17 12:04:34 +08:00
echo -e "\n[INFO] clean all nodes of cluster in 5s"
2019-03-13 20:47:53 +08:00
sleep 5
ansible-playbook $BASEPATH/99.clean.yml
rm -f $BASEPATH/.cluster/$CLUSTER/config
2019-03-14 12:38:33 +08:00
[ "$#" -gt 0 ] || { return 0; }
2019-03-13 20:47:53 +08:00
if [[ -n $1 && $1 == --purge ]];then
echo "[INFO] delete current context"
rm -rf $BASEPATH/.cluster/$CLUSTER
rm -rf $BASEPATH/hosts /root/.kube/*
echo "[INFO] change current context to default"
2019-03-14 12:38:33 +08:00
echo default > $BASEPATH/.cluster/current_cluster
install_context
2019-03-13 20:47:53 +08:00
fi
else
2019-03-17 12:04:34 +08:00
echo -e "\n[WARN] destroy aborted"; return 1;
2019-03-13 20:47:53 +08:00
fi
}
2019-03-22 08:38:45 +08:00
function start-aio(){
checkout aio
set +u
# Check ENV 'HOST_IP', if exist indecates running in a docker container, otherwise running in a host machine
if [[ -z $HOST_IP ]];then
# easzctl runs in a host machine, get host's ip
HOST_IF=$(ip route|grep default|cut -d' ' -f5)
HOST_IP=$(ip a|grep $HOST_IF|awk 'NR==2{print $2}'|cut -d'/' -f1)
fi
set -u
cp -f $BASEPATH/example/hosts.allinone.example.en $BASEPATH/hosts
sed -i "s/192.168.1.1/$HOST_IP/g" $BASEPATH/hosts
setup
}
### Main Lines ##################################################
BASEPATH=/etc/ansible
[ "$#" -gt 0 ] || { usage >&2; exit 2; }
case "$1" in
### in-cluster operations #####################
(add-node)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
ACTION="Action: add a k8s work node"
CMD="add-node $2"
;;
(add-master)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
ACTION="Action: add a k8s master node"
CMD="add-master $2"
;;
(add-etcd)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
ACTION="Action: add a etcd node"
CMD="add-etcd $2"
;;
(del-etcd)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
ACTION="Action: delete a etcd node"
CMD="del-etcd $2"
;;
(clean-node)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
ACTION="Action: clean a node"
CMD="clean-node $2"
;;
2019-03-26 08:56:52 +08:00
(upgrade)
ACTION="Action: upgrade the cluster"
CMD="upgrade"
;;
### cluster-wide operations #######################
(checkout)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
ACTION="Action: checkout cluster context"
CMD="checkout $2"
;;
2019-03-13 20:47:53 +08:00
(destroy)
ACTION="Action: destroy current cluster"
if [ "$#" -gt 1 ];then
CMD="destroy $2"
else
CMD="destroy"
fi
;;
2019-03-17 17:58:39 +08:00
(list)
ACTION="Action: list all of clusters managed"
CMD="list"
;;
(setup)
ACTION="Action: setup cluster with current context"
CMD="setup"
;;
2019-03-17 17:58:39 +08:00
(start-aio)
ACTION="Action: start an AllInOne cluster"
CMD="start-aio"
;;
(help)
[ "$#" -gt 1 ] || { usage >&2; exit 2; }
help-info $2
exit 0
;;
(*)
usage
exit 0
;;
esac
process_cmd