Skip to main content

Practical DevOps scripts


Table of contents

  1. Automated deployment
  2. Backup and restore
  3. Monitoring and alerts
  4. Log management
  5. System maintenance
  6. CI/CD scripts


1 - Automated deployment

Simple deployment script

#!/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

Zero-downtime deployment

#!/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
}

🔝 Back to table of contents



2 - Backup and restore

Complete backup script

#!/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

Restore script

#!/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 "$@"

🔝 Back to table of contents



3 - Monitoring and alerts

Monitoring script

#!/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

🔝 Back to table of contents



4 - Log management

Rotation and analysis

#!/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"
}

🔝 Back to table of contents



5 - System maintenance

Maintenance script

#!/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

🔝 Back to table of contents



6 - CI/CD scripts

Build script

#!/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

Test script

#!/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

🔝 Back to table of contents



Key takeaways

  • Deployment scripts with backup and healthcheck
  • Backups with rotation and verification
  • Monitoring with alerts (Slack, email)
  • Log management with analysis and rotation
  • Regular automated maintenance
  • CI/CD scripts for build, test, package

🔝 Back to table of contents


← Previous chapter | Next chapter: Exercises and projects →