Было в практике такое, что в кластер openshift (в его namespace) ходили все кому не лень, меняли там всякое, особенно скейлинг, а мы потом думали почему в этом месяце счет за AWS сильно больше обычного.
Лиды подумали, подумали, да решили дать задачку на реализацию «чего-либо» что могло взять заранее подготовленные файлы с количеством реплик, просчитанные под тот или иной контур и восстановить согласно этим файлам эталонное кол-во реплик, а лучше по крону это делать в конце рабочего дня или ночью.
Из этой затеи родился вот это bash скрипт, который позже был переписан на groovy дабы запускать в рамках jenkins, но об этом в другой статье, сейчас посмотрим на bash скрипт. Комментарии как всегда в коде.
#!/bin/bash
ns=$2
### Описываем собственно "эталонные" файлики
stop="microservice_stop.txt"
### Тут скейлинг просчитанный с учетом 20% нагрузки от продовой
twenty_persent="20_persent_up.txt"
full="full_up.txt"
statefulset_down="statefulset_down.txt"
statefulset_twenty_persent="statefulset_20.txt"
statefulset_full="statefulset_full.txt"
statefulset_user_custom="statefulset_user_custom.txt"
user_custom="user_custom.txt"
only_cadence="only_cadence.txt"
minus_cadence="minus_cadence.txt"
cronjob_state="cronjob_state_$ns.txt"
### На проекте использовался ldap, который должен был стартовать одним из первых
function waiting_ds389_pods {
oc project $ns
while [ "$(oc get pods -l app=ds389 -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}')" != "True True True" ]; do
sleep 10
echo "Waiting for ds389 up"
done
}
### Тоже самое, только для cadence workfkow
function waiting_cadence_pods {
oc project $ns
cadence=(cadence-frontend cadence-history cadence-matching cadence-worker)
while [[ True ]];do
cadence_frontend=$(oc get pod -l app=${cadence[0]} -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}')
cadence_history=$(oc get pod -l app=${cadence[1]} -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}')
cadence_matching=$(oc get pod -l app=${cadence[2]} -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}')
cadence_worker=$(oc get pod -l app=${cadence[3]} -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}')
if [[ $cadence_frontend && $cadence_history && $cadence_matching && $cadence_worker == "True" ]];then
break
else
echo "Waiting for cadence up"
sleep 10
fi;
done
}
## Получаем текущий стейт cronjob и записываем его в файл
function get_cronjob_state {
if [[ -f $cronjob_state ]];then
rm $cronjob_state
fi;
oc project $ns
for name in `oc get cronjob --no-headers|awk '{print $1}'`; do
state=$(oc get cronjob $name -o yaml |grep -w "suspend:"|awk '{print $2}')
echo "$name $state" >> $cronjob_state
done
}
### Вырубаем cronjobs
function disable_cronjob {
oc project $ns
for name in `oc get cronjob --no-headers|awk '{print $1}'`; do
oc patch cronjobs $name -p '{"spec" : {"suspend" : true }}'
done
}
### Поднимаем кронджобы из предыдущего стейта(тот самый файлик)
function cronjob_up {
oc project $ns
while read line;do
name=$(echo ${line}|awk '{print $1}')
state=$(echo ${line}|awk '{print $2}')
oc patch cronjobs $name -p '{"spec" : {"suspend" : '$state' }}'
done < "${cronjob_state}"
}
### С помощью этой функи скейлим statefulset приложения
scale_statefulset() {
while read line; do
dcName=$(echo ${line}|awk '{print $1}')
dcReplica=$(echo ${line}|awk '{print $2}')
($1 $dcName $2$dcReplica"}}")&
done < "$3"
wait
}
### Скейлим deploymentConfigs
scale_dc(){
while read line; do
dcName=$(echo ${line}|awk '{print $1}')
dcReplica=$(echo ${line}|awk '{print $2}')
($1 $dcName $2$dcReplica)&
done < "$3"
wait
}
### Тут останавливаем все используя предыдущие функции скейлинга для разных сущностей
stop() {
oc project $ns
scale_statefulset "oc patch statefulsets" "-p {\"spec\":{\"replicas\":" "${statefulset_down}"
scale_dc "oc scale dc" "--replicas=" "${stop}"
get_cronjob_state
disable_cronjob
}
### Стартуем все с 20% нагрузкой от прода
start_20() {
oc project $ns
scale_statefulset "oc patch statefulsets" "-p {\"spec\":{\"replicas\":" "${statefulset_twenty_persent}"
waiting_ds389_pods
cat $twenty_persent |grep -E "^cadence" > $only_cadence
scale_dc "oc scale dc" "--replicas=" "${only_cadence}"
waiting_cadence_pods
cat $twenty_persent |grep -v -E "^cadence" > $minus_cadence
scale_dc "oc scale dc" "--replicas=" "${minus_cadence}"
if [[ -f $cronjob_state ]];then
cronjob_up
fi;
}
### Стартуем вообще все)
start_all() {
oc project $ns
scale_statefulset "oc patch statefulsets" "-p {\"spec\":{\"replicas\":" "${statefulset_full}"
waiting_ds389_pods
cat $full |grep -E "^cadence" > $only_cadence
scale_dc "oc scale dc" "--replicas=" "${only_cadence}"
waiting_cadence_pods
cat $full |grep -v -E "^cadence" > $minus_cadence
scale_dc "oc scale dc" "--replicas=" "${minus_cadence}"
if [[ -f $cronjob_state ]];then
cronjob_up
fi;
}
### Это некий фич для запуска по кастомных файлам для скейлинга
start_custom() {
oc project $ns
scale_statefulset "oc patch statefulsets" "-p {\"spec\":{\"replicas\":" "${statefulset_user_custom}"
waiting_ds389_pods
cat $user_custom |grep -E "^cadence" > $only_cadence
scale_dc "oc scale dc" "--replicas=" "${only_cadence}"
waiting_cadence_pods
cat $user_custom |grep -v -E "^cadence" > $minus_cadence
scale_dc "oc scale dc" "--replicas=" "${minus_cadence}"
if [[ -f $cronjob_state ]];then
cronjob_up
fi;
}
case $1 in
start_20|stop|start_all|start_custom) $1;;
restart_all) stop; start_all;;
restart_20) stop; start_20;;
restart_custom) stop; start_custom;;
*) echo "Usage: $0 <start_20|start_all|start_custom|stop|restart_all|restart_20|restart_custom> namespace"; exit 1;;
esac
Вот и все. Скрипт успешно использовался, пока не был переписан на groovy и стал использоваться еще более успешно и удобно прямо из jenkins, но об этом в следующей статье.