Некоторые картинки не загружаются из РФ и РК, используйте VPN.

среда, 13 марта 2024 г.

PoSh Script Проверка web сервиса и перезапуск docker desktop

Сервис развернут контейнере (Docker desktop). Пожаловались, что иногда он вываливается и единственное решение - полный перезапуск wsl и "docker desktop". Пользователь в будуюшем будет переносить докеры в linux среду, а на текущий момент нужен костыль. Сразу оговорюсь, попытка создать службу docker провалилась, поэтому здесь решение совсем грязное. Но в самом скрипте есть пара интересных, для меня, моментов, поэтому кладу его здесь.

Скрипт проверяет ответ от web-сервиса (внешний адрес и порт), если его нету, то ждет N время, иначе выходит. По истечении N времени идет повторная проверка, если ответа нет, то идет проверка локальная, иначе выход. Если локальная проверка не прошла, то перезапускаем нужные службы и программы, иначе выходим.

Запускать я решил батником в task scheduller, чтобы не править каждый раз задачу. При этом задача должна выполнятся от имени администратора и БЕЗ привязки к пользователю, иначе Desktop manager не запустит контейнеры.


Два триггера


Для автозапуска Docker desktop при загрузке ПК настраиваем автовход пользователя  (control userpasswords2)  и автозапуск Docker desktop при входе в настройках программы. На данный момент это единственное рабочее решение из найденных :-(

Команда запуска в планировщике (окно скрипта скрыто)

%windir%\system32\cmd.exe
/c start /min C:\usr\WSLCheckStart.bat ^& exit

Будьте осторожны с таким методом скрытия окна. Этот метод открывает скрипт за пределами планировщика. Планировщик не может контролировать выполнение задачи, а значит возможно наложение нескольких задач. Для таких случаев рекомендуется проводить проверку в самом скрипте.

Батник:

@echo off
cd %~dp0
set "pathscript=%~dp0\WSLCheck.ps1"
set "pathlog=%~dp0\log"
mkdir %~dp0\log
IF EXIST %pathscript% (
  powershell.exe -NoProfile -ExecutionPolicy Bypass -File %pathscript% -Address "dev.site.com" -Port "443" -LocalPort "8080" -TimeOut 20 -TimeOutHTTP 10 -LogFile %pathlog%
) ELSE (
  echo "ERROR! %pathscript% not exist"
  echo "ERROR! %pathscript% not exist" >> WSLLogStart.log
)

Сам скрипт Powershell

#Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
#Set-ExecutionPolicy Undefined -Scope CurrentUser
# скрипт на входе принимает ряд параметров
# Address - строка - fqdn проверяемого ресурса
# Port - строка - внешний порт подключения
# LocalPort - строка - локальный порт. Так как скрипт запускается на сервере, где крутится сам сервис, то мы можем проверить доступность сервиса локально и  исключить ошибку доступа снаружи (нет интернета, сломался nginx, сломались dns записи)
# TimeOut - число - время в секундах между первой второй проверкой доступности сервиса снаружи. Если обе проверки провались, идет проверка локальной доступности
# TimeOutHTTP - число - время в секундах таймаут проверки доступности сайта 
# LogFilePath - строка - куда писать лог папка
# whatif - переключатель - аналог оригинального параметра whatif, в данном случае скрипт не выполняет перезапуск, а только говорит о том, что будет делать

param(
     [Parameter()]
     [string]$Address='web.site.com',
     [Parameter()]
     [string]$Port='443',
     [Parameter()]
     [string]$LocalPort='8080',
     [Parameter()]
     [int]$TimeOut='20',
	 [Parameter()]
     [int]$TimeOutHTTP='10',
     [Parameter()]
     [string]$LogFilePath="C:\usr\",
     [Parameter()]
     [switch]$whatif
)

# Требуется для сравнения внешнего и внутреннего резолва. Избежать такой ситуации мы не можем, а записать об этом информацию - можем
$DNSServer="8.8.8.8"
# Разрешенное время работы ПК без запущенного серивса. Избегаем ошибки когда система еще не запустилась, а скрипт уже работает
$AcceptUptime=600
# localhost может вернуть как  IPv4, так и IPv6 адрес, что может привести к ложному срабатыванию, если есть разные сервисы на одном порту, но по разным IP
$localhost="127.0.0.1"
$DockerDesktopPath="C:\Program Files\Docker\Docker\Docker Desktop.exe"
$DateTimeStamp=Get-Date -Format "dd.MM.yyyy"
$LogFile="$LogFilePath\$DateTimeStamp`_WSLCheck.log"

# простенькая функция логирования, если файл есть, то пишет в него
# можно было использовать tee-object, но этот командлет выдает ошибку при отсутствии папки для лога
# level - число - 
function Logging($Message,$Level=0,$CheckRepeat=$false){
	if ("$Message" -eq ""){
		return
	}
	$mLevel=switch($level){
		0 {New-Object psobject -Property @{label="[INF]";color="White"}}
		1 {New-Object psobject -Property @{label="[WAR]";color="Yellow"}}
		2 {New-Object psobject -Property @{label="[ERR]";color="Red"}}
	}
    $CurrentTimeStamp=Get-Date -Format "dd.MM.yyyy`tHH:mm:ss"
    Write-Host "$Message" -ForegroundColor  $mLevel.color
	
	if ($whatif){return}
    if (-not (Test-Path $LogFile)) {New-Item -Path $LogFile -ItemType File -ErrorAction SilentlyContinue}  
	if (Test-Path $LogFile) {
		$lastLine=""
		if ($CheckRepeat){
			$lastLine=$(Get-Content -Path $LogFile)[-1].Split("`t")[-1]
		}
		if ("$Message" -ne "$lastLine"){
			Write-Output("$CurrentTimeStamp`t$($mLevel.label)`t$Message") | Out-File $LogFile -Append
		}
	}
}

# Проверяем уровень прав пользователя
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if ( -Not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
	Write-Host "Error! Run this script as administrtor" -ForegroundColor Red
	exit
}

# Проверяем аптайм ПК и выходим, если он меньше $AcceptUptime
$uptime = $(Get-Date)-(Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
if ($uptime.TotalSeconds -lt $AcceptUptime){
	Logging "PC uptime less then $AcceptUptime secundes, break"
	exit
}

# Проверяем как резолвится имя сервиса
$ResolveDNSext=$(Resolve-DnsName -Name $Address -Server $DNSServer -DnsOnly).ip4address
$ResolveDNSint=$(Resolve-DnsName -Name $Address).ip4address
if ("$ResolveDNSext" -ne "$ResolveDNSint"){
	logging "Resolve error! $DNSServer resolved $address as $ResolveDNSext. Local returned $ResolveDNSint!" 1
}

# Функция проверки доступности сервиса на указанном порту. Построена таким образом, что при успешно подключении завершает скрипт ( таким образом мы исключаем громоздкие конструкции if else листингом ниже)
function TestConnect(){
	param(
		 [Parameter()]
		 [string]$Address,
		 [Parameter()]
		 [string]$Port,
		 [switch]$Web=$false,
		 [switch]$SSL=$true
		 
	)
	if ($Web){
		$protocol = if ($SSL) {"https://"} else {"http://"}
		$port = if (($SSL) -and ($port -ne 443)) {"`:$port"} else {""}
		$url="$protocol$address$port"
		try {
			$WebRequest=Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec $TimeOutHTTP
			$StatusCode=$WebRequest.Statuscode
		} catch {
			$StatusCode=$_.Exception.Response.StatusCode.value__
		}
		$Status=$("$StatusCode" -eq "200")
	} else {
		$Status=$(Test-NetConnection $Address -Port $Port -InformationLevel Quiet)
		$url="$Address`:$port"
	}
	
    if ($status){
        Logging "Service ($url) is available"
        exit
    }
	Logging "Service ($url) is unavailable" 1
}

# Сама программа
TestConnect -Address $Address -Port $Port -Web -SSL

# you are here because the last test failed

Logging "Wait $TimeOut seconds"

sleep $TimeOut

TestConnect -Address $Address -Port $Port -Web -SSL

# you are here because the second test failed

Logging "The service ($Address`:$port) has not been restored" 1
Logging "Check local service ($localhost`:$LocalPort)"

TestConnect -Address $localhost -Port $LocalPort -Web -SSL
Logging "The local service ($localhost`:$LocalPort) has not been restored" 1

# you are here because the third test failed

# Than onlyCheck 
function RestartProgramm($ProcessName,$PathExeFile){
	$processes = Get-Process "$ProcessName"

	if ($processes.Count -gt 0)
	{
		$processes | ForEach-Object {
			$_.Kill()
			$_.WaitForExit()
			logging "Process $($_.ProcessName) with PID $($_.id)`t ended"
		}
		logging "Waiting for all processes to shut down (5 sec)"
		sleep 5
	}
	
	Start-Process -FilePath $PathExeFile -OutVariable SvcMessage -ErrorVariable SvcMessageErr -ErrorAction SilentlyContinue
	Logging $SvcMessage
	Logging $SvcMessageErr 2
	return $($(Get-Process "$ProcessName").Count -gt 0)
}
function WSLRestart(){

    logging "Restart WSL Service (LxssManager)...."
    if (-not $whatif) {
        Restart-Service LxssManager -OutVariable SvcMessage -ErrorVariable SvcMessageErr -ErrorAction SilentlyContinue
		Logging $SvcMessage
		Logging $SvcMessageErr 2
    }

    Logging "Restart docker desktop..."
    if (-not $whatif) {

		if (RestartProgramm "*docker desktop*" "$DockerDesktopPath") {
			Logging "Docker desktop running!"
		} else {
			Logging "Docker desktop NOT running!" 2
		}
    }
}
WSLRestart

Комментариев нет:

Отправить комментарий