Scripts DevOps pratiques
Table des matières
- Déploiement automatisé
- Backup et restauration
- Monitoring et alertes
- Gestion des logs
- Maintenance système
- 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 →