Aller au contenu principal

Scripts DevOps pratiques


Table des matières

  1. Déploiement automatisé
  2. Backup et restauration
  3. Monitoring et alertes
  4. Gestion des logs
  5. Maintenance système
  6. Scripts CI/CD


1 - Déploiement automatisé

Script de déploiement simple

#!/bin/bash
set -euo pipefail

#######################
# Configuration
#######################
readonly APP_NAME="myapp"
readonly DEPLOY_DIR="/var/www/$APP_NAME"
readonly BACKUP_DIR="/var/backups/$APP_NAME"
readonly GIT_REPO="[email protected]:user/myapp.git"
readonly BRANCH="${1:-main}"

#######################
# Fonctions
#######################

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

backup_current() {
if [ -d "$DEPLOY_DIR" ]; then
local backup_name="${APP_NAME}_$(date '+%Y%m%d_%H%M%S')"
log "Backup vers $BACKUP_DIR/$backup_name"
cp -r "$DEPLOY_DIR" "$BACKUP_DIR/$backup_name"
fi
}

deploy() {
log "Déploiement de la branche: $BRANCH"

# Clone ou pull
if [ -d "$DEPLOY_DIR/.git" ]; then
cd "$DEPLOY_DIR"
git fetch origin
git checkout "$BRANCH"
git pull origin "$BRANCH"
else
git clone -b "$BRANCH" "$GIT_REPO" "$DEPLOY_DIR"
cd "$DEPLOY_DIR"
fi

# Install dependencies
if [ -f "package.json" ]; then
log "Installation des dépendances npm"
npm ci --production
fi

if [ -f "requirements.txt" ]; then
log "Installation des dépendances Python"
pip install -r requirements.txt
fi
}

restart_service() {
log "Redémarrage du service"
sudo systemctl restart "$APP_NAME" || true
}

healthcheck() {
log "Vérification de santé"
local max_attempts=30
local attempt=1

while [ $attempt -le $max_attempts ]; do
if curl -sf http://localhost:8080/health > /dev/null; then
log "Application OK"
return 0
fi
log "Tentative $attempt/$max_attempts..."
sleep 2
((attempt++))
done

log "ERREUR: L'application ne répond pas"
return 1
}

#######################
# Main
#######################

main() {
log "=== Début du déploiement ==="

backup_current
deploy
restart_service

if ! healthcheck; then
log "ROLLBACK nécessaire"
exit 1
fi

log "=== Déploiement terminé ==="
}

main

Déploiement zero-downtime

#!/bin/bash
set -euo pipefail

# Blue-Green deployment simplifié
readonly APP_DIR="/var/www"
readonly CURRENT_LINK="$APP_DIR/current"
readonly RELEASES_DIR="$APP_DIR/releases"

deploy_zero_downtime() {
local release_dir="$RELEASES_DIR/$(date '+%Y%m%d%H%M%S')"

# Préparer la nouvelle release
mkdir -p "$release_dir"
git clone "$GIT_REPO" "$release_dir"
cd "$release_dir"
npm ci --production
npm run build

# Basculer le lien symbolique (atomique)
ln -sfn "$release_dir" "$CURRENT_LINK"

# Recharger l'application
sudo systemctl reload nginx

# Nettoyer les anciennes releases (garder les 5 dernières)
ls -1dt "$RELEASES_DIR"/* | tail -n +6 | xargs -r rm -rf
}

🔝 Retour à la table des matières



2 - Backup et restauration

Script de backup complet

#!/bin/bash
set -euo pipefail

#######################
# Configuration
#######################
readonly BACKUP_BASE="/backup"
readonly DATE=$(date '+%Y%m%d')
readonly RETENTION_DAYS=30

# Ce qu'on sauvegarde
declare -A BACKUP_SOURCES=(
[web]="/var/www"
[config]="/etc"
[data]="/home"
)

# Base de données
readonly DB_NAME="myapp"
readonly DB_USER="backup"
readonly DB_PASS_FILE="/root/.db_password"

#######################
# Fonctions
#######################

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$BACKUP_BASE/backup.log"
}

backup_files() {
local name="$1"
local source="$2"
local dest="$BACKUP_BASE/$DATE/${name}.tar.gz"

log "Backup fichiers: $name"

mkdir -p "$(dirname "$dest")"
tar -czf "$dest" -C "$(dirname "$source")" "$(basename "$source")" 2>/dev/null || {
log "WARN: Certains fichiers ignorés dans $name"
}

log " -> $(du -h "$dest" | cut -f1)"
}

backup_database() {
local dest="$BACKUP_BASE/$DATE/database.sql.gz"

log "Backup base de données: $DB_NAME"

PGPASSWORD=$(cat "$DB_PASS_FILE") pg_dump \
-U "$DB_USER" \
-h localhost \
"$DB_NAME" | gzip > "$dest"

log " -> $(du -h "$dest" | cut -f1)"
}

cleanup_old_backups() {
log "Nettoyage des backups de plus de $RETENTION_DAYS jours"

find "$BACKUP_BASE" -maxdepth 1 -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
}

verify_backup() {
local dir="$BACKUP_BASE/$DATE"

log "Vérification des backups"

for file in "$dir"/*.tar.gz; do
if tar -tzf "$file" > /dev/null 2>&1; then
log " OK: $(basename "$file")"
else
log " ERREUR: $(basename "$file")"
return 1
fi
done

if gunzip -t "$dir/database.sql.gz" 2>/dev/null; then
log " OK: database.sql.gz"
else
log " ERREUR: database.sql.gz"
return 1
fi
}

#######################
# Main
#######################

main() {
log "=== Début du backup ==="

# Backup des fichiers
for name in "${!BACKUP_SOURCES[@]}"; do
backup_files "$name" "${BACKUP_SOURCES[$name]}"
done

# Backup de la base
backup_database

# Vérification
verify_backup

# Nettoyage
cleanup_old_backups

# Résumé
log "=== Backup terminé ==="
log "Taille totale: $(du -sh "$BACKUP_BASE/$DATE" | cut -f1)"
}

main

Script de restauration

#!/bin/bash
set -euo pipefail

restore_backup() {
local backup_date="${1:?Usage: $0 <date YYYYMMDD>}"
local backup_dir="/backup/$backup_date"

[ -d "$backup_dir" ] || {
echo "Backup non trouvé: $backup_dir"
exit 1
}

echo "Restauration du backup: $backup_date"
read -p "Confirmer? (y/N) " -n 1 -r
echo
[[ $REPLY =~ ^[Yy]$ ]] || exit 0

# Restaurer les fichiers
for archive in "$backup_dir"/*.tar.gz; do
echo "Restauration: $(basename "$archive")"
tar -xzf "$archive" -C /
done

# Restaurer la base
echo "Restauration base de données"
gunzip -c "$backup_dir/database.sql.gz" | psql -U postgres myapp

echo "Restauration terminée"
}

restore_backup "$@"

🔝 Retour à la table des matières



3 - Monitoring et alertes

Script de monitoring

#!/bin/bash
set -uo pipefail

#######################
# Configuration
#######################
readonly SLACK_WEBHOOK="${SLACK_WEBHOOK:-}"
readonly EMAIL_TO="${ALERT_EMAIL:[email protected]}"

# Seuils
readonly DISK_THRESHOLD=80
readonly MEMORY_THRESHOLD=90
readonly LOAD_THRESHOLD=4

#######################
# Fonctions
#######################

send_alert() {
local level="$1"
local message="$2"

echo "[$level] $message"

# Slack
if [ -n "$SLACK_WEBHOOK" ]; then
curl -sf -X POST -H 'Content-type: application/json' \
-d "{\"text\":\"[$level] $(hostname): $message\"}" \
"$SLACK_WEBHOOK" > /dev/null || true
fi

# Email
echo "$message" | mail -s "[$level] Alert from $(hostname)" "$EMAIL_TO" 2>/dev/null || true
}

check_disk() {
local usage
usage=$(df / | awk 'NR==2 {print int($5)}')

if [ "$usage" -gt "$DISK_THRESHOLD" ]; then
send_alert "WARNING" "Disque: ${usage}% utilisé (seuil: ${DISK_THRESHOLD}%)"
return 1
fi
return 0
}

check_memory() {
local usage
usage=$(free | awk '/Mem:/ {printf "%.0f", $3/$2 * 100}')

if [ "$usage" -gt "$MEMORY_THRESHOLD" ]; then
send_alert "WARNING" "Mémoire: ${usage}% utilisée (seuil: ${MEMORY_THRESHOLD}%)"
return 1
fi
return 0
}

check_load() {
local load
load=$(cat /proc/loadavg | cut -d' ' -f1)

if (( $(echo "$load > $LOAD_THRESHOLD" | bc -l) )); then
send_alert "WARNING" "Load: $load (seuil: $LOAD_THRESHOLD)"
return 1
fi
return 0
}

check_service() {
local service="$1"

if ! systemctl is-active --quiet "$service"; then
send_alert "CRITICAL" "Service $service est DOWN!"
return 1
fi
return 0
}

check_url() {
local url="$1"
local name="${2:-$url}"

if ! curl -sf --connect-timeout 10 "$url" > /dev/null; then
send_alert "CRITICAL" "$name ne répond pas!"
return 1
fi
return 0
}

#######################
# Main
#######################

main() {
local status=0

check_disk || status=1
check_memory || status=1
check_load || status=1

# Services
for service in nginx postgresql; do
check_service "$service" || status=1
done

# URLs
check_url "http://localhost/health" "App Health" || status=1

exit $status
}

main

🔝 Retour à la table des matières



4 - Gestion des logs

Rotation et analyse

#!/bin/bash
set -euo pipefail

# Rotation manuelle des logs
rotate_logs() {
local log_dir="${1:-/var/log/myapp}"
local max_files="${2:-7}"

for log in "$log_dir"/*.log; do
[ -f "$log" ] || continue

# Rotation
for i in $(seq $((max_files-1)) -1 1); do
[ -f "${log}.$i" ] && mv "${log}.$i" "${log}.$((i+1))"
done

[ -f "$log" ] && mv "$log" "${log}.1"
touch "$log"
done

# Compression des anciens
find "$log_dir" -name "*.log.[2-9]" -exec gzip {} \;
}

# Analyse des erreurs
analyze_errors() {
local log_file="$1"
local period="${2:-1 hour ago}"

echo "=== Erreurs depuis $period ==="

awk -v since="$(date -d "$period" '+%Y-%m-%d %H:%M')" '
$1 " " $2 >= since && /ERROR|CRITICAL|FATAL/ {
count[$0]++
}
END {
for (err in count) {
printf "%5d: %s\n", count[err], err
}
}
' "$log_file" | sort -rn | head -20
}

# Top des IPs
top_ips() {
local access_log="${1:-/var/log/nginx/access.log}"
local n="${2:-10}"

echo "=== Top $n IPs ==="
awk '{print $1}' "$access_log" | sort | uniq -c | sort -rn | head -n "$n"
}

🔝 Retour à la table des matières



5 - Maintenance système

Script de maintenance

#!/bin/bash
set -euo pipefail

log() {
echo "[$(date '+%H:%M:%S')] $*"
}

# Mise à jour système
update_system() {
log "Mise à jour des packages"
apt-get update -qq
apt-get upgrade -y -qq
apt-get autoremove -y -qq
}

# Nettoyage Docker
cleanup_docker() {
if command -v docker &> /dev/null; then
log "Nettoyage Docker"
docker system prune -af --volumes || true
fi
}

# Nettoyage fichiers temporaires
cleanup_temp() {
log "Nettoyage fichiers temporaires"
find /tmp -type f -atime +7 -delete 2>/dev/null || true
find /var/tmp -type f -atime +30 -delete 2>/dev/null || true
}

# Nettoyage logs
cleanup_logs() {
log "Nettoyage logs anciens"
find /var/log -name "*.gz" -mtime +30 -delete 2>/dev/null || true
journalctl --vacuum-time=7d || true
}

# Vérification disque
check_disk_health() {
log "Vérification santé disque"
for disk in /dev/sd[a-z]; do
[ -e "$disk" ] || continue
smartctl -H "$disk" 2>/dev/null || true
done
}

# Main
main() {
log "=== Maintenance système ==="

update_system
cleanup_docker
cleanup_temp
cleanup_logs
check_disk_health

log "=== Maintenance terminée ==="
}

main

🔝 Retour à la table des matières



6 - Scripts CI/CD

Script de build

#!/bin/bash
set -euo pipefail

readonly BUILD_DIR="build"
readonly VERSION="${CI_COMMIT_TAG:-dev-$(git rev-parse --short HEAD)}"

build() {
echo "Building version: $VERSION"

rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"

# Build application
npm ci
npm run build

# Copy to build dir
cp -r dist/* "$BUILD_DIR/"

# Create version file
echo "$VERSION" > "$BUILD_DIR/VERSION"
}

test_app() {
echo "Running tests..."
npm test
}

package() {
echo "Creating package..."
tar -czf "app-${VERSION}.tar.gz" -C "$BUILD_DIR" .
}

main() {
build
test_app
package
echo "Build complete: app-${VERSION}.tar.gz"
}

main

Script de test

#!/bin/bash
set -euo pipefail

run_tests() {
echo "=== Running Tests ==="

# Unit tests
echo "Unit tests..."
npm run test:unit

# Integration tests
echo "Integration tests..."
npm run test:integration

# Lint
echo "Linting..."
npm run lint

# Security audit
echo "Security audit..."
npm audit --audit-level=high
}

main() {
run_tests
echo "All tests passed!"
}

main

🔝 Retour à la table des matières



Points clés à retenir

  • Scripts de déploiement avec backup et healthcheck
  • Backups avec rotation et vérification
  • Monitoring avec alertes (Slack, email)
  • Gestion des logs avec analyse et rotation
  • Maintenance automatisée régulière
  • Scripts CI/CD pour build, test, package

🔝 Retour à la table des matières


← Chapitre précédent | Chapitre suivant : Exercices et projets →