Имеется достаточно интересная ифраструктура одного проекта (UPD: описана в посте AWS: билд Java + Maven + Docker + Packer + Terraform), в котором деплой API-приложения выполняется скриптом, описанным ниже.
Его задача — запустить EC2 инстансы, объединённые в blue AutoScale группу, подключить её к Elastic Load Balancer-у (ELB), отключить от ELB green-группу, перезапустить в ней инстансы, подключить её обратно к ELB и отключить blue-группу.
Сам скрипт, как решение задачи, является скорее антипаттерном того, как надо делать — куда логичнее тут смотрелся бы Terraform, который используется во время деплоя для обновления launch-configuration AutoScale группы, с новыми AMI ID, которые являются результатом билда с новой версией приложения. Инстансы, запускаемые скриптом используют этот новый launch-configuration, и запускаются с новым приложением. Скрипт запускается groovy-скриптом из docker-контейнера в Jenkins, который сам работает в docker-контейнере.
Ещё лучше было бы мигрировать весь проект на AWS ECS, но выбор между Groovy, Terraform или bash + AWS CLI обуславливался временем, а ECS отложен на будущие фазы проекта, т.к. его (пока) нет в Китае, с которым предстоит работать.
Вообще про blue/green деплой AutoScale групп можно почитать интересные документы вот тут>>> и тут>>>.
UPD: сам скрипт (спустя почти год после написания поста) доступен в Github тут>>>.
Основной недостаток скрипта и вообще подхода к деплою тут — это не «чистый» blue-green деплой, т.к. инстансы в Blue группе после деплоя уничтожаются, и окружение остаётся только с одной версией приложения, и второй момент — вместо запуска и удаления инстансов в Bleu группе — можно было бы подключать Blue группу к ELB, и оставлять её работать, а Green — отключить, уменьшив таким образом кол-во выполняемые операций и ускорив деплой.
Тем не менее — сам по себе скрипт выглядит любопытно и содержит много реальных примеров работы с AWS CLI.
В скрипте используются 10 основных глобальных переменных, из которых 5 в «боевой» версии передаются в докер-контейнер Jenkins-ом опциями командной строки, заданных в билде:
#!/usr/bin/env bash #set -xe ################################################### ### See comments in main() block at the bottom. ### ################################################### # $1 - for getopts() option AWS_ACCESS_KEY_ID=$2 AWS_SECRET_ACCESS_KEY=$3 AWS_REGION=$4 ENVIRONMENT=$5 MIN_SIZE_GREEN=$6 #export AWS_DEFAULT_PROFILE=PROJECTNAME #export AWS_DEFAULT_REGION=eu-west-1 #AWS_ACCESS_KEY_ID= #AWS_SECRET_ACCESS_KEY= #AWS_REGION=eu-west-1 BLUE_ASG="$ENVIRONMENT-api-blue-asg" GREEN_ASG="$ENVIRONMENT-api-green-asg" ELB="$ENVIRONMENT-api-elb" SCALE_UP_RULE="$ENVIRONMENT-api-scale-up" SCALE_DOWN_RULE="$ENVIRONMENT-api-scale-down" export PATH=$PATH:"/home/aws/aws/env/bin/" export AWS_DEFAULT_REGION=$AWS_REGION ...
Скрипт умеет принимать 3 опции, которые обрабатываются с помощью getopts():
...
HELP="Specify one of the following options: -d - simple deploy, -b - Production Blue deploy, -g - Production Green deploy"
get_params () {
if [[ -z $* ]];then
echo -e "\n$HELP\n"
exit 1
fi
deploy=
deploy_blue=
deploy_green=
while getopts "dbgh" opt; do
case $opt in
d)
deploy=1
;;
b)
deploy_blue=1
;;
g)
deploy_green=1
;;
h)
echo "$HELP"
;;
esac
done
}
...
-d— simple deploy: выполняет Production Blue deploy и Production Green deploy, один за другим. Используется для Dev-деплоя, где не требуется подтверждение между переключениями в ELB blue и green групп.-b— Production Blue deploy: устанавливает переменнуюdeploy_blue== 1, в результате чего вызывается функцияdeploy_blue().-g— Production Green deploy: аналогично — устанавливает переменнуюdeploy_green== 1, в результате чего вызывается функцияdeploy_green().
Далее в скрипте описываются различные вспомогательные функции, которые вызывают AWS CLI для выполнения действий, а замыкающими описываются три основных функции — deploy_blue(), deploy_green(), и последняя — main(), которые последовательно вызывают все функции, описанные в скрипте выше.
Выполнение скрипта начинается в самом его низу, с вызова функций get_params() и main():
... # check for -d (simple deploy), -b (deploy_blue()) or -g (deploy_green()) option first get_params $@ # execute main() which will call appropriate function depending on variables specified by get_params() main && echo -e "\nDeployment finished successfully.\n" ...
get_params() принимает переданные Jenkins-ом опцию ($@ — все аргументы, переданные скрипту), и глобально задаёт одну из трёх переменных (надо было сделать через аргумент в main()), в зависимости от чего в main() выполняется одна из трёх последовательностей:
...
[[ $deploy ]] && { deploy_blue && deploy_green; echo -e "Result code: $?\n"; }
[[ $deploy_blue ]] && { deploy_blue; echo -e "Result code: $?\n"; }
[[ $deploy_green ]] && { deploy_green; echo -e "Result code: $?\n"; }
...
Содержание
Деплой blue-группы
main()
Полностью функция main() с кратким описанием процесса всего процесса билда и деплоя приложения выглядит так:
...
main () {
# Build and deploy workflow
#
# 1 Maven
# 1.1 Maven build jar archives with an application.
# 1.2 Maven builds Docker images 1 per service with those jar-files included.
# 1.3 Maven push those images to the Artifactory Private Docker registry.
# 2 Packer
# 2.1 Packer takes $base_ami ID and creates new EC2 instance.
# 2.2 Installs Docker there and pulls Docker images with the latest code built by Maven.
# 2.3 Builds new AMI with those images included.
# 3 Terraform
# 3.1 Terraform checks all its configs and current infrastructure state plus TF's state-files.
# 3.2 Regarding to "most_recent = true" in api.tf - TF will see differences between current EC2 instances AMI ID
# and ones found as "latest" in AWS account's AMIs list, as there are new AMIs created by Packer.
# 3.4 Terraform updates Launch Configs (LC) for AutoScaling groups (ASG) with new AMI IDs.
# 4 Deploy
# 4.1 Add new EC2 instance to Blue AutoScale group.
# 4.2 Attach Blue ASG to the Elastic Load Balancer (ELB).
# 4.3 Detach Green ASG - traffic will be served by the Blue Group's EC2 instance, started with latest AMI ID.
# 4.4 Terminate Green ASG instances.
# 4.5 AutoScale will create an appropriate number of new instances.
# 4.6 Attach Green ASG to the ELB.
# 4.7 Detach Blue ASG.
# 4.8 Terminate Blue ASG instances.
[[ $deploy ]] && { deploy_blue && deploy_green; echo -e "Result code: $?\n"; }
[[ $deploy_blue ]] && { deploy_blue; echo -e "Result code: $?\n"; }
[[ $deploy_green ]] && { deploy_green; echo -e "Result code: $?\n"; }
}
...
После чего вызывается сначала функция deploy_blue(), которая обновляет EC2-инстансы в AutoScale blue-группе, а за ней deploy_green(), которая обновляет инстансы в green-группе.
Собственно функция deploy_blue() выглядит так:
...
deploy_blue () {
if blue_asg_status $BLUE_ASG; then
# add 1 instance in Blue group wich will start with latest builded AMI
echo -e "\n[BLUE] Blue ASG is empty, executing ScaleUp policy...\n"
asg_scale_up $BLUE_ASG && echo -e "Scaled up to 1 instance.\n" || exit 1
# to prevent new instances creation during deployment
echo -e "[BLUE] Blue ASG updating MAX value to 1...\n"
asg_set_max_size $BLUE_ASG 1 && echo -e "Done.\n" || exit 1
# wait when Blue EC2 will start
echo -e "[BLUE] Checking Blue ASG health...\n"
asg_health $BLUE_ASG || exit 1
# to prevent new instances creation during deployment
echo -e "[GREEN] Green ASG updating MIN value to 1...\n"
asg_set_min_size $GREEN_ASG 1 && echo -e "Done.\n" || exit 1
# to prevent new instances creation during deployment
echo -e "[GREEN] Green ASG updating MAX value to 1...\n"
asg_set_max_size $GREEN_ASG 1 && echo -e "Done.\n" || exit 1
# add Blue EC2 under ELB's traffic control
echo -e "[BLUE] Attaching Blue to ELB...\n"
asg_attach $BLUE_ASG && echo -e "Blue ASG attached.\n" || exit 1
# to avoid instance termitation during it's "Green" role
echo -e "[BLUE] Setting ScaleIn protection...\n"
asg_set_protect $BLUE_ASG && echo -e "Done\n" || exit 1
# sleep before check ELB - intanse will be added not immediately
echo -e "[BLUE] Checking ELB intastances health for Blue instance Up...\n"
sleep 30
# ELB health checks :8080/health
# Thus - it will check untill Gateway service will not start
elb_health $BLUE_ASG || exit 1
echo -e "\n[GREEN] Detaching Green from ELB.\n"
asg_detach $GREEN_ASG && echo -e "Done.\n" || exit 1
# sleep before check ELB - Green intanse will be removed from ELB not immediately
# ask Verify after
## sleep 30
echo -e "Blue ASG deploy finished successfully.\n"
else
echo -e "ERROR - all instances in Blue ASG must be terminated and Desired value == 0. Exit.\n"
exit 1
fi
}
...
blue_asg_status()
Ход выполнения deploy_blue() достаточно простой, и начинается с проверки результата выполнения функции blue_asg_status(), которой аргументом передаётся имя AutoScale группы со значением dev, stage или prod, в зависимости от переменой $ENVIRONMENT, заданной из аргументов Jenkins-а:
... BLUE_ASG="$ENVIRONMENT-api-blue-asg" ...
Вызов blue_asg_status():
...
if blue_asg_status $BLUE_ASG; then
...
Задача функции blue_asg_status() — проверить, не имеется ли запущенных инстансов в blue-группе (которая после деплоя остаётся пустой).
Выглядит она так:
...
blue_asg_status () {
local asg_name=$1
local instances_running=$(get_instances_running $asg_name)
if [[ ! -z $instances_running ]]; then
echo -e "\nThere is running instances in the Blue ASG: $instances_running\n"
return 1
fi
}
...
Если переменная $instances_running не равна нулю — то билд останавливается с ненулевым результатом выполнения. Значение $instances_running зависит от результата выполнения функции $get_instances_running(), которая вызывает AWS CLI, подключется к AWS-аккаунту, и проверяет количество запущенных инстансов, а результат передаёт обратно в blue_asg_status():
...
get_instances_running () {
local asg_name=$1
local instances_running=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $asg_name --query '[AutoScalingGroups[*].Instances[*].InstanceId]' --output text)
echo $instances_running
}
...
Если же $instances_running == 0, то deploy_blue() продолжает выполнение, и вызывает следующую функцию — asg_scale_up():
...
# add 1 instance in Blue group wich will start with latest builded AMI
echo -e "\n[BLUE] Blue ASG is empty, executing ScaleUp policy...\n"
asg_scale_up $BLUE_ASG && echo -e "Scaled up to 1 instance.\n" || exit 1
...
Собственно ход выполнения main() и deploy_blue() в output Jenkins-а выглядит так:
... [EU-api-staging-build] Running shell script + ./scripts/api_eu_deploy.sh -b **** **** eu-west-1 staging 2 [BLUE] Blue ASG is empty, executing ScaleUp policy... ...
Функция asg_scale_up() применяет к blue AutoScale группе политику масштабирования из переменной $SCALE_UP_RULE:
... SCALE_UP_RULE="$ENVIRONMENT-api-scale-up" ...
Правило с именем $ENVIRONMENT-api-scale-up создаётся, обновляется или не изменяется Terraform-ом во время деплоя.
Политика $ENVIRONMENT-api-scale-up увеличивает количество инстансов в blue-группе (которая «приходит» в деплой пустой) на число, зависящее от окружения (dev — 1, staging, prod — 2):
...
asg_scale_up () {
local asg_name=$1
aws autoscaling execute-policy --auto-scaling-group-name $asg_name --policy-name $SCALE_UP_RULE
}
...
asg_set_max_size()
Следующей вызывается asg_set_max_size():
...
# to prevent new instances creation during deployment
echo -e "[BLUE] Blue ASG updating MAX value to 1...\n"
asg_set_max_size $BLUE_ASG 1 && echo -e "Done.\n" || exit 1
...
Сама функция:
...
asg_set_max_size () {
local asg_name=$1
local max_size=$2
aws autoscaling update-auto-scaling-group --auto-scaling-group-name $asg_name --max-size $max_size
}
...
Она задаёт масксимальное количество EC2-инстансов в blue группе равным единице, после чего AWS AutoScale запускает новую машину из последнего AMI с приложением. Так как это blue-группа — то для экономии времени в ней достаточно одной машины.
asg_health()
После этого — deploy_blue() вызывает функцию asg_health():
...
# wait when Blue EC2 will start
echo -e "[BLUE] Checking Blue ASG health...\n"
asg_health $BLUE_ASG || exit 1
...
Задача её — дождаться запуска машины (статус == InService) за $max * $timeout секунд, после чего выполнение цикла прерывается оператором break, и управление выполнения возвращается к deploy_blue() с кодом 0:
...
asg_health () {
local at=0
local max=10
local timeout=10
local asg_name=$1
while [ $at -lt $max ]; do
local health=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $asg_name --query [AutoScalingGroups[*].Instances[*].LifecycleState] --output text)
if [[ $health == "InService" ]]; then
echo -e "\n\n$asg_name instance(s) OK, ready to attach to ELB: $health\n"
break
else
echo "($at/$max $timeout sec) $asg_name instance(s) not ready yet: $health"
fi
((at++))
sleep $timeout
done
}
...
Выполнение asg_scale_up(), asg_set_max_size() и asg_health() выглядит так:
…
Scaled up to 1 instance.[BLUE] Blue ASG updating MAX value to 1…
Done.
[BLUE] Checking Blue ASG health…
(0/10 10 sec) staging-api-blue-asg instance(s) not ready yet:
(1/10 10 sec) staging-api-blue-asg instance(s) not ready yet:
(2/10 10 sec) staging-api-blue-asg instance(s) not ready yet: Pending
(3/10 10 sec) staging-api-blue-asg instance(s) not ready yet: Pending
(4/10 10 sec) staging-api-blue-asg instance(s) not ready yet: Pendingstaging-api-blue-asg instance(s) OK, ready to attach to ELB: InService
…
asg_set_min_size()
Далее вызывается asg_set_min_size():
...
# to prevent new instances creation during deployment
echo -e "[GREEN] Green ASG updating MIN value to 1...\n"
asg_set_min_size $GREEN_ASG 1 && echo -e "Done.\n" || exit 1
...
Она уменьшает текущее количество инстасов в green-группе до 1 (во избежаение проблем с таймаутами в циклах далее):
...
asg_set_min_size () {
local asg_name=$1
local min_size=$2
aws autoscaling update-auto-scaling-group --auto-scaling-group-name $asg_name --min-size $min_size
}
...
asg_set_max_size()
Аналогично вызывается функция asg_set_max_size(), но для значения Max instances number для green-группы:
...
asg_set_max_size () {
local asg_name=$1
local max_size=$2
aws autoscaling update-auto-scaling-group --auto-scaling-group-name $asg_name --max-size $max_size
}
...
asg_attach()
Вызов:
...
# add Blue EC2 under ELB's traffic control
echo -e "[BLUE] Attaching Blue to ELB...\n"
asg_attach $BLUE_ASG && echo -e "Blue ASG attached.\n" || exit 1
...
asg_attach() подключает blue-группу с инстансом к Elastic Load Balancer-у, который начинает перенаправлять трафик на новую машину:
...
asg_attach () {
local asg_name=$1
aws autoscaling attach-load-balancers --auto-scaling-group-name $asg_name --load-balancer-names $ELB
}
...
asg_set_protect()
После подключения blue-группы — можно отключать green, но сначала — лучше установить Scale in Protection для инстанса в blue-группе, что выполняется с помощью asg_set_protect():
...
# to avoid instance termitation during it's "Green" role
echo -e "[BLUE] Setting ScaleIn protection...\n"
asg_set_protect $BLUE_ASG && echo -e "Done\n" || exit 1
...
Тело функции:
...
asg_set_protect () {
local asg_name=$1
local instance_to_protect=$(get_instances_running $asg_name)
aws autoscaling set-instance-protection --instance-ids $instance_to_protect --protected-from-scale-in --auto-scaling-group-name $asg_name
}
...
Она получает Instance ID из функции get_instances_running()
...
get_instances_running () {
local asg_name=$1
local instances_running=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $asg_name --query '[AutoScalingGroups[*].Instances[*].InstanceId]' --output text)
echo $instances_running
}
...
elb_health()
Вызов:
...
# sleep before check ELB - intanse will be added not immediately
echo -e "[BLUE] Checking ELB intastances health for Blue instance Up...\n"
sleep 30
# ELB health checks :8080/health
# Thus - it will check untill Gateway service will not start
elb_health $BLUE_ASG || exit 1
...
Упоминал о циклах и таймаутах чуть выше, пример такой функции (зачем два раза [ $at -lt $max ]? только сейчас заметил):
...
elb_health () {
local out_state="OutOfService"
local at=0
local max=30
local timeout=30
while [ $at -lt $max ]; do
local elb_health=$(aws elb describe-instance-health --load-balancer-name $ELB --query '[InstanceStates[*].State]' --output text)
if [[ $elb_health =~ $out_state ]]; then
echo "($at/$max $timeout sec) Some intances still $elb_health"
else
echo -e "\n\nIntance up and running: $elb_health"
break
fi
((at++))
sleep $timeout
if [ $at == $max ]; then
echo "\nERROR: max attempts reached.\n"
exit 1
fi
done
}
...
Так как только для иницилизации приложения на Java Spring Framework уходит порядка двух минут — то билд замирает в ожидании, пока в ELB не посчитает, что только что подключенный blue-инстанс уже готов обрабатывать запросы. Такое решение ELB принимает на основании результатов health-проверок к инстансу на порт 8080, после чего статус инстанса сменится с OutOfService на InService.
asg_detach()
Теперь ELB готов к отключению green-группы, что бы начать перенаправлять весь трафик на машину из blue-группы.
Отключение выполняется вызовом asg_detach():
...
echo -e "\n[GREEN] Detaching Green from ELB.\n"
asg_detach $GREEN_ASG && echo -e "Done.\n" || exit 1
...
В результатах билда asg_set_min_size(), asg_set_max_size(), asg_attach(), asg_set_protect(), elb_health() и asg_detach() выглядят так:
…
[GREEN] Green ASG updating MIN value to 1…Done.
[GREEN] Green ASG updating MAX value to 1…
Done.
[BLUE] Attaching Blue to ELB…
Blue ASG attached.
[BLUE] Setting ScaleIn protection…
Done
[BLUE] Checking ELB intastances health for Blue instance Up…
(0/30 30 sec) Some intances still InService OutOfService
…
(14/30 30 sec) Some intances still InService OutOfServiceIntance up and running: InService InService
[GREEN] Detaching Green from ELB.
Done.
Blue ASG deploy finished successfully.
Result code: 0
…
Следующим шагом main() после подтверждения вызывает deploy_green().
Vefiry step для Jenkins реализован в groovy-скрипте:
...
def deploy_prod_verify() {
stage 'Verify'
input id: 'Deploy', message: 'Is Blue node fine? Proceed with Green node deployment?', ok: 'Deploy!'
}
...
И выглядит так:
Деплой green-группы
deploy_green()
deploy_green() аналогична debloy_blue() и использует те же функции плюс 2 дополнительных:
...
deploy_green () {
# terminate Gren's instance
echo -e "[GREEN] Updating Green ASG instances.\n"
green_asg_terminate $GREEN_ASG
echo -e "\n[GREEN] Waiting for Green ASG instance termination...\n"
slee?p 30
# new instances must be started here by ASG
echo -e "[GREEN] Checking Green ASG health.\n"
asg_health $GREEN_ASG || exit 1
# add Green to ELB
echo -e "[GREEN] Attaching Green to ELB.\n"
asg_attach $GREEN_ASG && echo -e "Green ASG attached\n" || exit 1
# wait for the Gateway service UP state (/health)
echo -e "[GREEN] Checking ELB intastances health for Green instance Up...\n"
sleep 30
elb_health $GREEN_ASG || exit 1
# detach Blue - Green now is Green
echo -e "\n[BLUE] Detaching Blue...\n"
asg_detach $BLUE_ASG && echo -e "Done.\n" || exit 1
# remove protection before ScaleIn rule will be executed
echo -e "[BLUE] Removing ScaleIn protection...\n"
asg_remove_protect $BLUE_ASG && echo -e "Done\n" || exit 1
# ScaleIn rule will be executed :-)
echo -e "[BLUE] Scaling Blue in...\n"
asg_scale_down $BLUE_ASG && echo -e "Scale in to 0 instance - done.\n" || { echo "ERROR: can't execute ScaleDown policy. Exit."; exit 1; }
# set Green ASG Max instances back to 8
echo -e "[GREEN] Green ASG restoring MAX value to 8...\n"
asg_set_max_size $GREEN_ASG 8 && echo -e "Done.\n" || exit 1
# set Green ASG Min instances back to ${MIN_SIZE_GREEN} from eu-west-ENV.groovy
echo -e "[GREEN] Green ASG restoring MIN value to $MIN_SIZE_GREEN...\n"
asg_set_min_size $GREEN_ASG $MIN_SIZE_GREEN && echo -e "Done.\n" || exit 1
echo -e "Green ASG deploy finished successfully.\n"
}
...
green_asg_terminate()
Вызов:
...
deploy_green () {
# terminate Gren's instance
echo -e "[GREEN] Updating Green ASG instances.\n"
green_asg_terminate $GREEN_ASG
...
Она начинает своё выполнение с вызова green_asg_terminate(), задача которой — остановить инстансы green-группы со старым кодом, на место которых потом будут добавлены новые, из свежесобранных AMI:
...
green_asg_terminate () {
local asg_name=$1
local instances_running=$(get_instances_running $asg_name)
instance_terminate "$instances_running"
echo -e "\nChecking $instances_running state...\n"
# be sure instance already terminated before proceed
instance_state "$instances_running"
}
...
get_instances_running() уже упоминалась в deploy_blue(), а instance_terminate() выглядит так:
...
instance_terminate () {
local instances_running=$1
echo -e "Terminating instances $instances_running..."
for instance in $instances_running; do
echo -e "\nStopping instance $instance...\n"
aws ec2 terminate-instances --instance-ids $instance || exit 1
done
}
...
После того как старые инстансы были остановлены — AWS AutoScale увеличиет их количество, с новым кодом приложения.
Следующие функции уже упоминались:
...
echo -e "\n[GREEN] Waiting for Green ASG instance termination...\n"
sleep 30
# new instances must be started here by ASG
echo -e "[GREEN] Checking Green ASG health.\n"
asg_health $GREEN_ASG || exit 1
# add Green to ELB
echo -e "[GREEN] Attaching Green to ELB.\n"
asg_attach $GREEN_ASG && echo -e "Green ASG attached\n" || exit 1
# wait for the Gateway service UP state (/health)
echo -e "[GREEN] Checking ELB intastances health for Green instance Up...\n"
sleep 30
elb_health $GREEN_ASG || exit 1
# detach Blue - Green now is Green
echo -e "\n[BLUE] Detaching Blue...\n"
asg_detach $BLUE_ASG && echo -e "Done.\n" || exit 1
...
asg_health() ожидает состояния всех инстансов в green-группе == InService, asg_attach() подключает её к ELB, elb_health() ожидает ответа от всех инстансов в ELB на запросы к порту 8080 а asg_detach() отключает blue-группу.
Результат в Jenkins:
…
[GREEN] Updating Green ASG instances.Terminating instances i-064dbc31791a0ad2c…
Stopping instance i-064dbc31791a0ad2c…
{
«TerminatingInstances»: [
{
«InstanceId»: «i-064dbc31791a0ad2c»,
«CurrentState»: {
«Code»: 32,
«Name»: «shutting-down»
},
«PreviousState»: {
«Code»: 16,
«Name»: «running»
}
}
]
}Checking i-064dbc31791a0ad2c state…
(0/10 20 sec) Instance i-064dbc31791a0ad2c still running…
(1/10 20 sec) Instance i-064dbc31791a0ad2c still running…
(2/10 20 sec) Instance i-064dbc31791a0ad2c still running…Instance i-064dbc31791a0ad2c stopped.
[GREEN] Waiting for Green ASG instance termination…
[GREEN] Checking Green ASG health.
(0/10 10 sec) staging-api-green-asg instance(s) not ready yet: Pending
(1/10 10 sec) staging-api-green-asg instance(s) not ready yet: Pending
(2/10 10 sec) staging-api-green-asg instance(s) not ready yet: Pendingstaging-api-green-asg instance(s) OK, ready to attach to ELB: InService
[GREEN] Attaching Green to ELB.
Green ASG attached
[GREEN] Checking ELB intastances health for Green instance Up…
(0/30 30 sec) Some intances still OutOfService InService
…
(15/30 30 sec) Some intances still OutOfService InServiceIntance up and running: InService InService
[BLUE] Detaching Blue…
Done.
…
asg_remove_protect()
...
# remove protection before ScaleIn rule will be executed
echo -e "[BLUE] Removing ScaleIn protection...\n"
asg_remove_protect $BLUE_ASG && echo -e "Done\n" || exit 1
...
asg_remove_protect() снимает ScaleIn защиту с инстанса в blue-группе для её «зачистки» перед завершением деплоя:
...
asg_remove_protect () {
local asg_name=$1
local instance_to_protect=$(get_instances_running $asg_name)
aws autoscaling set-instance-protection --instance-ids $instance_to_protect --no-protected-from-scale-in --auto-scaling-group-name $asg_name
}
...
asg_scale_down()
Далее blue-группа уменьшается до 0 машин:
...
# ScaleIn rule will be executed
echo -e "[BLUE] Scaling Blue in...\n"
asg_scale_down $BLUE_ASG && echo -e "Scale in to 0 instance - done.\n" || { echo "ERROR: can't execute ScaleDown policy. Exit."; exit 1; }
...
asg_set_max_size() и asg_set_min_size() уже были — восстанавливаются исходные данные green-группы для Max intances до 8, а Min — до 2:
...
# set Green ASG Max instances back to 8
echo -e "[GREEN] Green ASG restoring MAX value to 8...\n"
asg_set_max_size $GREEN_ASG 8 && echo -e "Done.\n" || exit 1
# set Green ASG Min instances back to ${MIN_SIZE_GREEN} from eu-west-ENV.groovy
echo -e "[GREEN] Green ASG restoring MIN value to $MIN_SIZE_GREEN...\n"
asg_set_min_size $GREEN_ASG $MIN_SIZE_GREEN && echo -e "Done.\n" || exit 1
echo -e "Green ASG deploy finished successfully.\n"
}
...
В Jenkins это выглядит так:
…
[BLUE] Removing ScaleIn protection…Done
[BLUE] Scaling Blue in…
Scale in to 0 instance — done.
[GREEN] Green ASG restoring MAX value to 8…
Done.
[GREEN] Green ASG restoring MIN value to 2…
Done.
Green ASG deploy finished successfully.
Result code: 0
[Pipeline] }
[Pipeline] // withCredentials
[Pipeline] }
$ docker stop —time=1 e59c0fac136f8222e9b83da116931a4956cb9f4a80428b5844fdb271557de179
$ docker rm -f e59c0fac136f8222e9b83da116931a4956cb9f4a80428b5844fdb271557de179
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
Как-то так…






