/
monik
/
lab2_cicd_monitoring
Обзор
Документация
Войти
/
monik
/
lab2_cicd_monitoring
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scripts/ci/performance-test.sh
504 строки
14 KB
Kirill Kazarin
first_commit
24 авг 2025, 15:25
24 авг 2025, 15:25
5454277
Код
Авторство
О чём код?
#!/bin/bash # Скрипт тестирования производительности # Выполняет нагрузочное тестирование с помощью k6 set -euo pipefail # Цвета для вывода RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Функция логирования log() { local level=$1 shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') case $level in "INFO") echo -e "${BLUE}[${timestamp}] INFO:${NC} ${message}" ;; "WARN") echo -e "${YELLOW}[${timestamp}] WARN:${NC} ${message}" ;; "ERROR") echo -e "${RED}[${timestamp}] ERROR:${NC} ${message}" ;; "SUCCESS") echo -e "${GREEN}[${timestamp}] SUCCESS:${NC} ${message}" ;; *) echo -e "[${timestamp}] ${message}" ;; esac } # Функция показа справки show_help() { cat << EOF Использование: $0 [ОПЦИИ] Скрипт тестирования производительности для CI/CD. ОПЦИИ: --smoke Smoke тест (быстрая проверка) --load Нагрузочное тестирование --stress Стресс-тестирование --spike Spike тестирование --target URL URL для тестирования (по умолчанию: http://localhost:80) --duration TIME Продолжительность теста (по умолчанию: 60s) --vus NUM Количество виртуальных пользователей (по умолчанию: 10) --threshold P95 Пороговое значение P95 (по умолчанию: 2000ms) --output FILE Файл для сохранения результатов -v, --verbose Подробный вывод -h, --help Показать эту справку ПРИМЕРЫ: $0 --smoke $0 --load --target http://app.example.com --duration 300s --vus 50 $0 --stress --threshold 5000ms --output results.json EOF } # Переменные по умолчанию TEST_TYPE="smoke" TARGET_URL="http://localhost:80" DURATION="60s" VUS=10 THRESHOLD_P95=2000 OUTPUT_FILE="" VERBOSE=false # Парсинг аргументов while [[ $# -gt 0 ]]; do case $1 in --smoke) TEST_TYPE="smoke" shift ;; --load) TEST_TYPE="load" shift ;; --stress) TEST_TYPE="stress" shift ;; --spike) TEST_TYPE="spike" shift ;; --target) TARGET_URL="$2" shift 2 ;; --duration) DURATION="$2" shift 2 ;; --vus) VUS="$2" shift 2 ;; --threshold) THRESHOLD_P95="$2" shift 2 ;; --output) OUTPUT_FILE="$2" shift 2 ;; -v|--verbose) VERBOSE=true shift ;; -h|--help) show_help exit 0 ;; -*) log "ERROR" "Неизвестная опция: $1" show_help exit 1 ;; *) log "ERROR" "Неизвестный аргумент: $1" show_help exit 1 ;; esac done # Функция проверки наличия k6 check_k6() { if ! command -v k6 &> /dev/null; then log "ERROR" "k6 не установлен" log "INFO" "Для установки выполните:" log "INFO" " macOS: brew install k6" log "INFO" " Ubuntu: sudo apt-get install k6" log "INFO" " Или скачайте с https://k6.io/docs/getting-started/installation/" exit 1 fi log "INFO" "k6 версия: $(k6 version)" } # Функция проверки доступности целевого URL check_target() { log "INFO" "Проверка доступности целевого URL: $TARGET_URL" if ! curl -s --max-time 10 --retry 3 "$TARGET_URL" > /dev/null; then log "ERROR" "Целевой URL недоступен: $TARGET_URL" log "INFO" "Убедитесь, что приложение запущено и доступно" exit 1 fi log "SUCCESS" "Целевой URL доступен" } # Функция создания конфигурации теста create_test_config() { local test_file="k6-test-${TEST_TYPE}.js" case $TEST_TYPE in "smoke") cat > "$test_file" << EOF import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate } from 'k6/metrics'; const errorRate = new Rate('errors'); export const options = { stages: [ { duration: '10s', target: 1 }, { duration: '20s', target: 5 }, { duration: '10s', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<${THRESHOLD_P95}'], http_req_failed: ['rate<0.1'], errors: ['rate<0.1'], }, vus: 5, duration: '40s', }; export default function () { const baseUrl = '${TARGET_URL}'; // Тест главной страницы const mainPageResponse = http.get(\`\${baseUrl}/\`); check(mainPageResponse, { 'main page status is 200': (r) => r.status === 200, 'main page loads fast': (r) => r.timings.duration < 1000, }); if (mainPageResponse.status !== 200) { errorRate.add(1); } sleep(1); // Тест health check const healthResponse = http.get(\`\${baseUrl}/health\`); check(healthResponse, { 'health check status is 200': (r) => r.status === 200, 'health check loads fast': (r) => r.timings.duration < 500, }); if (healthResponse.status !== 200) { errorRate.add(1); } sleep(1); } EOF ;; "load") cat > "$test_file" << EOF import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate } from 'k6/metrics'; const errorRate = new Rate('errors'); export const options = { stages: [ { duration: '60s', target: ${VUS} }, { duration: '${DURATION}', target: ${VUS} }, { duration: '60s', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<${THRESHOLD_P95}'], http_req_failed: ['rate<0.05'], errors: ['rate<0.05'], }, vus: ${VUS}, }; export default function () { const baseUrl = '${TARGET_URL}'; // Тест главной страницы const mainPageResponse = http.get(\`\${baseUrl}/\`); check(mainPageResponse, { 'main page status is 200': (r) => r.status === 200, 'main page loads fast': (r) => r.timings.duration < 2000, }); if (mainPageResponse.status !== 200) { errorRate.add(1); } sleep(1); // Тест health check const healthResponse = http.get(\`\${baseUrl}/health\`); check(healthResponse, { 'health check status is 200': (r) => r.status === 200, 'health check loads fast': (r) => r.timings.duration < 1000, }); if (healthResponse.status !== 200) { errorRate.add(1); } sleep(2); // Тест статических ресурсов const cssResponse = http.get(\`\${baseUrl}/src/style.css\`); check(cssResponse, { 'CSS status is 200': (r) => r.status === 200, 'CSS loads fast': (r) => r.timings.duration < 500, }); sleep(1); } EOF ;; "stress") cat > "$test_file" << EOF import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate } from 'k6/metrics'; const errorRate = new Rate('errors'); export const options = { stages: [ { duration: '60s', target: ${VUS} }, { duration: '120s', target: $((VUS * 2)) }, { duration: '120s', target: $((VUS * 4)) }, { duration: '120s', target: $((VUS * 8)) }, { duration: '60s', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<${THRESHOLD_P95}'], http_req_failed: ['rate<0.1'], errors: ['rate<0.1'], }, vus: ${VUS}, }; export default function () { const baseUrl = '${TARGET_URL}'; // Тест главной страницы const mainPageResponse = http.get(\`\${baseUrl}/\`); check(mainPageResponse, { 'main page status is 200': (r) => r.status === 200, }); if (mainPageResponse.status !== 200) { errorRate.add(1); } sleep(1); // Тест health check const healthResponse = http.get(\`\${baseUrl}/health\`); check(healthResponse, { 'health check status is 200': (r) => r.status === 200, }); if (healthResponse.status !== 200) { errorRate.add(1); } sleep(1); } EOF ;; "spike") cat > "$test_file" << EOF import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate } from 'k6/metrics'; const errorRate = new Rate('errors'); export const options = { stages: [ { duration: '60s', target: ${VUS} }, { duration: '30s', target: $((VUS * 10)) }, { duration: '60s', target: ${VUS} }, { duration: '30s', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<${THRESHOLD_P95}'], http_req_failed: ['rate<0.1'], errors: ['rate<0.1'], }, vus: ${VUS}, }; export default function () { const baseUrl = '${TARGET_URL}'; // Тест главной страницы const mainPageResponse = http.get(\`\${baseUrl}/\`); check(mainPageResponse, { 'main page status is 200': (r) => r.status === 200, }); if (mainPageResponse.status !== 200) { errorRate.add(1); } sleep(1); // Тест health check const healthResponse = http.get(\`\${baseUrl}/health\`); check(healthResponse, { 'health check status is 200': (r) => r.status === 200, }); if (healthResponse.status !== 200) { errorRate.add(1); } sleep(1); } EOF ;; *) log "ERROR" "Неизвестный тип теста: $TEST_TYPE" exit 1 ;; esac echo "$test_file" } # Функция выполнения теста run_test() { local test_file=$1 log "INFO" "Начинаю выполнение $TEST_TYPE теста..." log "INFO" "Целевой URL: $TARGET_URL" log "INFO" "Продолжительность: $DURATION" log "INFO" "Виртуальных пользователей: $VUS" log "INFO" "Порог P95: ${THRESHOLD_P95}ms" local k6_cmd="k6 run" if [[ -n "$OUTPUT_FILE" ]]; then k6_cmd="$k6_cmd --out json=$OUTPUT_FILE" fi if [[ "$VERBOSE" == "true" ]]; then k6_cmd="$k6_cmd --verbose" fi k6_cmd="$k6_cmd $test_file" log "INFO" "Выполняю команду: $k6_cmd" if eval "$k6_cmd"; then log "SUCCESS" "Тест производительности завершен успешно" if [[ -n "$OUTPUT_FILE" && -f "$OUTPUT_FILE" ]]; then log "INFO" "Результаты сохранены в $OUTPUT_FILE" fi else log "ERROR" "Тест производительности завершился с ошибкой" exit 1 fi } # Функция анализа результатов analyze_results() { if [[ -n "$OUTPUT_FILE" && -f "$OUTPUT_FILE" ]]; then log "INFO" "Анализ результатов теста..." if command -v jq &> /dev/null; then local total_requests=$(jq '.metrics.http_reqs.values.count' "$OUTPUT_FILE" 2>/dev/null || echo "0") local failed_requests=$(jq '.metrics.http_req_failed.values.rate' "$OUTPUT_FILE" 2>/dev/null || echo "0") local avg_response_time=$(jq '.metrics.http_req_duration.values.avg' "$OUTPUT_FILE" 2>/dev/null || echo "0") local p95_response_time=$(jq '.metrics.http_req_duration.values["p(95)"]' "$OUTPUT_FILE" 2>/dev/null || echo "0") log "INFO" "Результаты теста:" log "INFO" " Всего запросов: $total_requests" log "INFO" " Процент ошибок: $(echo "$failed_requests * 100" | bc -l 2>/dev/null || echo "0")%" log "INFO" " Среднее время ответа: ${avg_response_time}ms" log "INFO" " P95 время ответа: ${p95_response_time}ms" # Проверяем пороговые значения if (( $(echo "$p95_response_time > $THRESHOLD_P95" | bc -l) )); then log "WARN" "P95 время ответа превышает пороговое значение" else log "SUCCESS" "P95 время ответа в пределах нормы" fi else log "WARN" "jq не установлен, не удалось проанализировать результаты" fi fi } # Функция очистки cleanup() { local test_file=$1 if [[ -f "$test_file" ]]; then rm -f "$test_file" log "INFO" "Временный файл теста удален: $test_file" fi } # Основная функция main() { log "INFO" "Запуск тестирования производительности..." # Проверяем k6 check_k6 # Проверяем целевой URL check_target # Создаем конфигурацию теста local test_file=$(create_test_config) # Выполняем тест run_test "$test_file" # Анализируем результаты analyze_results # Очищаем временные файлы cleanup "$test_file" log "SUCCESS" "Тестирование производительности завершено" } # Обработка сигналов trap 'log "ERROR" "Скрипт прерван пользователем"; exit 130' INT TERM # Запуск основной функции main "$@"