Я изо всех сил пытался найти лучший способ решить эту проблему для сценария bash. У меня есть команда, которая будет проверять группы серверов на время их безотказной работы в минутах. Я хочу перейти к следующей группе перезагрузок только после того, как все серверы будут работать в течение 5 минут, но также хочу убедиться, что они не работали более часа на случай, если перезагрузка не произойдет.
Первоначально я пытался настроить цикл while, который продолжал бы выдавать команду для проверки времени безотказной работы и отправки вывода в массив. Я пытаюсь понять, как вы можете перебирать массив, пока все элементы этого массива не будут больше 5 и меньше. Я даже не добился успеха в первой проверке больше 5. Возможно ли вообще постоянно записывать в массив и выполнять арифметические проверки для каждого значения в массиве, чтобы все значения должны быть больше X в цикле while? Количество серверов, которые будут помещать свое текущее время безотказной работы в массив, варьируется для каждой группы, поэтому количество значений в массиве не всегда будет одинаковым.
Является ли массив даже правильным способом сделать это? Я бы привел примеры того, что я пробовал до сих пор, но это огромный беспорядок, и я думаю, что лучше всего начать с нуля, просто попросив ввода.
Вывод команды, которую я запускаю для подтягивания времени безотказной работы, выглядит примерно так:
1
2
1
4
3
2
Редактировать
Благодаря предоставленной помощи я смог получить функциональное доказательство концепции для этого, и я в восторге. Вот это на случай, если это может помочь любому, кто попытается сделать что-то подобное в будущем. Проблема заключалась в том, что мы используем AWS SSM для всех исправлений наших серверов Windows, и много раз, когда SSM приказывает серверам перезагрузиться после исправления, агенту SSM требуется много времени для регистрации. Это замедляет весь наш процесс, который сейчас выполняется вручную. десятки групп патчей. Много раз нам приходится идти и вручную проверять, действительно ли сервер перезагрузился после того, как мы сказали ему об этом из SSM, чтобы мы знали, что можем начать перезагрузку для следующей группы исправлений. Благодаря этому мы сможем создать единый сценарий, который выполняет перезагрузку для наших групп исправлений в правильном порядке и проверяет правильность перезагрузки серверов, прежде чем переходить к следующей группе.
#!/bin/bash
### The purpose of this script is to automate the execution of commands required to reboot groups of AWS Windows servers utilizing SSM while also verifying their uptime and only continuing on to the next group once the previous has reached X # of minutes. This solves the problems of AWS SSM Agents not properly checking in with SSM post-reboot.
patchGroups=(01 02 03) # array containing the values of the RebootGroup tag
for group in "${patchGroups[@]}"
do
printf "Rebooting Patch Group %q\n" "$group"
aws ec2 reboot-instances --instance-ids `aws ec2 describe-instances --filters "Name=tag:RebootGroup,Values=$group" --query 'Reservations[].Instances[].InstanceId' --output text`
sleep 2m
unset passed failed serverList # wipe arrays
declare -A passed failed serverList # declare associative arrays
serverList=$(aws ec2 describe-instances --filter "Name=tag:RebootGroup,Values=$group" --query 'Reservations[*].Instances[*].[InstanceId]' --output text)
for server in ${serverList} # loop through list of servers
do
failed["${server}"]=0 # add to the failed[] array
done
while [[ "${#failed[@]}" -gt 0 ]] # loop while number of servers in the failed[] array is greater than 0
do
for server in "${!failed[@]}" # loop through servers in the failed[] array
do
ssmID=$(aws ssm send-command --document-name "AWS-RunPowerShellScript" --document-version "1" --targets "[{\"Key\":\"InstanceIds\",\"Values\":[\"$server\"]}]" --parameters '{"commands":["$wmi = Get-WmiObject -Class Win32_OperatingSystem ","$uptimeMinutes = ($wmi.ConvertToDateTime($wmi.LocalDateTime)-$wmi.ConvertToDateTime($wmi.LastBootUpTime) | select-object -expandproperty \"TotalMinutes\")","[int]$uptimeMinutes"],"workingDirectory":[""],"executionTimeout":["3600"]}' --timeout-seconds 600 --max-concurrency "50" --max-errors "0" --region us-west-2 --output text --query "Command.CommandId")
sleep 5
uptime=$(aws ssm list-command-invocations --command-id "$ssmID" --details --query 'CommandInvocations[].CommandPlugins[].Output' --output text | sed 's/\r$//')
printf "Checking instance ID %q\n" "$server"
printf "Value of uptime is = %q\n" "$uptime"
# if uptime is within our 'success' window then move server to passed[] array
if [[ "${uptime}" -ge 3 && "${uptime}" -lt 60 ]]
then
passed["${server}"] = "${uptime}" # add to passed[] array
printf "Server with instance ID %q has successfully rebooted.\n" "$server"
unset failed["${server}"] # remove from failed[] array
fi
done
# display current status (edit/remove as desired)
printf "\n++++++++++++++ successful reboots\n"
printf "%s\n" "${!passed[@]}" | sort -n
printf "\n++++++++++++++ failed reboot\n"
for server in ${!failed[@]}
do
printf "%s - %s (mins)\n" "${server}" "${failed[${server}]}"
done | sort -n
printf "\n"
sleep 60 # adjust as necessary
done
done
Насколько большой huge mess? нам нужно что-то для начала ... как вы получаете время безотказной работы и как вы сохраняете это в массиве? выберите небольшое количество серверов (например, 5-10), заполните свой массив, а затем запустите typeset -p array_name, чтобы мы могли увидеть, что находится в массиве; вам нужно повторно тестировать все серверы при каждом проходе или только те, которые (пока что) находятся за пределами допустимого диапазона времени? ${#array_name[@]} должен указать количество элементов массива ... сравните это со счетчиком серверов, которые «прошли» ваш тест?
@ Hammond95 Только что обновлено, чтобы включить пример вывода времени безотказной работы. Извиняюсь, что не включил это изначально.
@markp-fuso Я не думаю, что мне нужно будет повторно тестировать все серверы при каждом проходе. Мне нравится ваша идея потенциального создания счетчика серверов, которые не прошли проверку, и проверки их только на предмет того, превышает ли их время безотказной работы 5 и меньше 60. Затем выход из цикла возможен только после того, как все прошли обе проверки.
Единица измерения для этого списка чисел (1 2 1 4 3 2), секунды? минут? как вы управляете списком серверов... хранящимся в массиве? хранится в файле?; опять же, насколько велика huge mess и можете ли вы опубликовать минимальную версию своего кода, которая представляет вашу деятельность? подумав об этом еще немного ... ассоциативный массив, где индекс - это имя сервера, а значение - последнее «время безотказной работы» (uptime[server1]=3 (мин)); предполагая основной тип цикла while true, внутренний цикл будет перебирать индексы/значения массива... и выходить из основного цикла while, когда counter=0
Альтернатива... все серверы изначально загружены в массив out-of-range; по мере прохождения теста удалять из массива out-of-range и в массив in-range; когда в массиве out-of-range больше нет записей ... перейти к следующему набору серверов ...
@markp-fuso еще раз спасибо. Я обновил исходный пост, чтобы показать сценарий проверки концепции, за создание которого я должен вам 98% кредита. Без вашей помощи вряд ли бы туда попал. Я ценю тщательность ваших ответов и то, как быстро вы смогли что-то состряпать с нуля.
@ChrisSmith рад, что смог помочь, и спасибо за подробную информацию о том, чего вы пытаетесь достичь; не уверен, как переформулировать его, но... возможно, вы захотите посмотреть, сможете ли вы изменить тему вопроса на что-то более подходящее для того, что вы пытаетесь сделать (например, что-то более описательное, чем "как определить, если число находится между двумя другими числами?) :-)
@markp-fuso Сделано и сделано
Похоже, вам нужно постоянно переоценивать выходные данные времени безотказной работы, чтобы получить нужные вам данные, поэтому массив или другая переменная могут просто застрять. Подумайте об этом функционально (как в функциях). Вам нужна функция, которая проверяет, находится ли время безотказной работы в пределах, которые вы хотите, только один раз. Затем вам нужно периодически запускать эту функцию. В случае успеха вы запускаете перезагрузку. Если это не удается, вы позволяете ему повторить попытку позже.
Рассмотрим этот код:
uptime_in_bounds() {
local min = "$1"
local max = "$2"
local uptime_secs
# The first value in /proc/uptime is the number of seconds the
# system has been up. We have to truncate it to an integer…
read -r uptime_float _ < /proc/uptime
uptime_secs = "${uptime_float%.*}"
# A shell function reflects the exit status of its last command.
# This function "succeeds" if the uptime_secs is between min and max.
(( min < uptime_secs && max > uptime_secs ))
}
if uptime_in_bounds 300 3600; then
sudo reboot # or whatever
fi
Как я могу использовать это именно? Например, моя первая группа серверов, которую я перезагружаю после исправления, сообщит что-то вроде «1 2 1 4 3 2» Я хотел бы продолжить перезагрузку моей следующей группы серверов только после того, как время работы каждого из этих серверов сообщит обратно. число больше 5.
Общая идея ... вероятно, потребуется некоторая настройка в зависимости от того, как OP отслеживает серверы, получает время безотказной работы и т. д...
# for a given set of servers, and assuming stored in variable ${server_list} ...
unset passed failed # wipe arrays
declare -A passed failed # declare associative arrays
for server in ${server_list} # loop through list of servers
do
failed["${server}"]=0 # add to the failed[] array
done
while [[ "${#failed[@]}" -gt 0 ]] # loop while number of servers in the failed[] array is greater than 0
do
for server in "${!failed[@]}" # loop through servers in the failed[] array
do
uptime=$( some_command_to_get_uptime_for_server "${server}" )
# if uptime is within our 'success' window then move server to passed[] array
if [[ "${uptime}" -gt 5 && "${uptime}" -lt 60 ]]
then
passed["${server}"] = "${uptime}" # add to passed[] array
unset failed["${server}"] # remove from failed[] array
else
failed["${server}"] = "${uptime}"
fi
done
# display current status (edit/remove as desired)
printf "\n++++++++++++++ successful reboots\n"
printf "%s\n" "${!passed[@]}" | sort -n
printf "\n++++++++++++++ failed reboot\n"
for server in ${!failed[@]}
do
printf "%s - %s (mins)\n" "${server}" "${failed[${server}]}"
done | sort -n
printf "\n"
sleep 30 # adjust as necessary
done
ПРИМЕЧАНИЯ:
${server_list}
for
, чтобы правильно заполнить массив failed[]
${server}
while
будет продолжаться «слишком долго»${uptime}
не находится в диапазоне 5-60 минут, OP может добавить блок else
для выполнения некоторых других операций для проблемного ${server}
Маркп-фусо, спасибо. Это фантастика. Очень хорошо изложено, и я думаю, что это действительно поможет мне найти окончательное решение. Это чрезвычайно полезно, особенно когда я просто предлагал общую проблему и не имел никакого существующего кода для работы. Я отчитаюсь о своих результатах.
Не могли бы вы предоставить входные данные для вашего скрипта?