104 commandes réellement tapées, chaque option expliquée — classées par besoin
Rechercher, traiter en lot, archiver — une option mal retenue coûte souvent un fichier supprimé ou des répertoires cachés oubliés
-l format long : droits, propriétaire, taille, date
-a inclure . et .. ainsi que tous les fichiers cachés
-h tailles avec unité (1.2K au lieu de 1234), uniquement avec -l
-t tri par date de modification, le plus récent en haut
-r inverse le tri ; avec -t, le plus ancien en haut
-S tri par taille décroissante — plus direct que -t pour trouver ce qui occupe
--time-style=long-iso affiche 2026-10-29 09:18, plus lisible que le format par défaut
# fichier le plus récent en haut, avec taille et date ls -laht --time-style=long-iso # trouver les plus gros consommateurs d'espace (-S) ls -lhS /var/log | head
-name correspond au nom de fichier, sensible à la casse ; -iname l'ignore
-type f fichiers seuls, -type d répertoires, -type l liens symboliques
-mtime +7 modifié depuis plus de 7 jours ; -mmin -30 dans les 30 dernières minutes
-size +50M plus de 50M ; -size -1k moins de 1k
-maxdepth 1 sans récursion : à ajouter en premier lors d'un diagnostic
-ls exécute ls -dils sur chaque résultat : taille, inode et date d'un coup
-delete supprime directement (répertoires vides seulement) ; -exec ... \; exécute une commande par résultat
-prune ignore un répertoire, obligatoirement avec -path
-o signifie « ou » pour enchaîner les conditions ; -print force l'affichage (requis avec -prune)
# gros fichiers modifiés dans les 30 dernières minutes (diagnostic d'une explosion du disque) find /var -type f -mmin -30 -size +50M -ls # supprimer en lot les logs de plus de 7 jours find /var/log -name '*.log.*' -mtime +7 -delete # ignorer .git et node_modules avant de chercher find . -path ./node_modules -prune -o -name '*.js' -print
-r récursif ; -R récursif en suivant les liens symboliques
-i ignore la casse ; -n numéros de ligne ; -v inverse, seulement les lignes non correspondantes
-E expressions régulières étendues (| exige -E, sinon il est traité comme un littéral)
-l n'affiche que les noms de fichiers, pour cibler avant de lire
-A3 / -B3 / -C3 affichent 3 lignes après / avant / autour d'une correspondance
--include='*.conf' restreint à un suffixe ; --exclude-dir ignore des répertoires
-w mot entier : cat ne remonte plus category
-rn est le duo quotidien : récursif plus numéros de ligne, sinon on ne sait pas où regarder
# chercher un upstream dans la config nginx, avec 3 lignes de contexte grep -rn -C3 'upstream' /etc/nginx --include='*.conf' # plusieurs mots-clés, n'importe lequel suffit (obligatoire : -E) grep -rnE 'timeout|refused|reset' /var/log/app/error.log # lister seulement les noms de fichiers contenant la config, puis les examiner un par un grep -rli 'proxy_pass' /etc/nginx
-c créer ; -x extraire ; -t lister sans extraire ; -z gzip ; -j bzip2
-f prend le nom de l'archive, donc -f toujours en dernier
-v détaille le traitement ; très pénalisant sur une grosse archive, à rediriger
-C extrait dans un répertoire, qui doit déjà exister
--exclude exclut des chemins à l'archivage ; le motif porte sur les chemins relatifs
-czf / -xzf / -tzf sont les trois combinaisons à retenir
Toujours -t avant extraction : évite qu'une tarbomb écrase le répertoire courant
# archiver en excluant les répertoires de cache tar -czf app.tar.gz --exclude='node_modules' --exclude='.git' -C /srv app # inspecter le contenu d'abord, puis extraire vers le répertoire voulu tar -tzf app.tar.gz | head -20 tar -xzf app.tar.gz -C /tmp/restore
rm -rf supprime récursivement sans confirmation — avec une variable vide, rm -rf "$DIR/" devient rm -rf /
-f force sans demander ; -r descend dans les répertoires : ensemble, c'est le duo dangereux
cp -a mode archive : conserve droits, dates, liens symboliques — l'option des sauvegardes
cp -n n'écrase jamais (no-clobber) ; idem pour mv -n
mv entre systèmes de fichiers fait copie puis suppression, sans atomicité : une interruption laisse les deux côtés incomplets
Un fichier supprimé alors qu'un processus l'ouvre garde son espace jusqu'à la fin de ce processus
test -n "$DIR" vérifie que la variable n'est pas vide — à faire avant tout rm
-- arrête l'analyse des options, pour qu'un nom commençant par - ne soit pas pris pour une option
# suppression sûre : vérifier d'abord que la variable n'est pas vide, puis supprimer [ -n "$DIR" ] && rm -rf -- "$DIR" # sauvegarde complète en conservant les attributs cp -a /srv/app /backup/app-$(date +%F)
df -h taux d'occupation par point de montage ; -i occupation des inodes (une table pleine bloque aussi l'écriture)
du -sh taille totale d'un répertoire ; -s un seul total, -h lisible
du -sh * | sort -h du plus gros au plus petit — sort -h comprend 1K/2M/3G
--max-depth=1 un seul niveau, bien plus rapide qu'une récursion complète
Un gros écart entre du et df signifie presque toujours des fichiers supprimés mais encore ouverts
2>/dev/null supprime les erreurs de permission, qui noieraient la sortie
# descendre niveau par niveau depuis la racine, entrer dès qu'un niveau grossit soudainement du -h --max-depth=1 / 2>/dev/null | sort -h | tail -10 # fichiers supprimés mais encore détenus par un processus (utile quand df est plein et du faible) lsof +L1 2>/dev/null | head
ln -s CIBLE LIEN — la cible vient en premier ; l'inverse crée un lien cassé
-s lien symbolique ; sans -s, lien matériel (même système de fichiers, pas les répertoires)
-f écrase un lien existant
-n avec -f remplace un lien qui pointe vers un répertoire au lieu d'y imbriquer le nouveau
-sfn est la combinaison standard pour basculer une release (current vers la nouvelle)
Un lien symbolique stocke un chemin et casse si la cible bouge ; un lien matériel partage l'inode
ln -sfn /srv/app/releases/20261029 /srv/app/current
stat donne taille, inode, droits et trois horodatages — la réponse directe à « ce fichier a-t-il vraiment été modifié »
-c format personnalisé : %y mtime, %s octets, %U:%G propriétaire, %a droits en octal, %n nom
-L suit le lien symbolique et décrit la cible (sans lui, on décrit le lien)
Trois dates : Access lecture, Modify contenu modifié, Change métadonnées (droits/propriétaire) modifiées
-f informations du système de fichiers (taille de bloc, total d'inodes), complémentaire de df
touch -d '2026-10-01 08:00' FICHIER fixe la mtime ; à coupler avec find -newer pour borner les changements
file donne le type réel, plus fiable que l'extension
stat -c '%a' est le moyen le plus rapide de vérifier les bits de droits
stat -c '%y %s %U:%G %a %n' /etc/nginx/nginx.conf stat -L -c '%y %s' /srv/app/current # voir le fichier réel pointé par le lien symbolique file /usr/local/bin/app
Éditer des configurations, agréger par colonne, construire des lignes de commande — tout ce qui touche à 200 lignes revient à sed/awk
-i modifie le fichier sur place — en production, lancer d'abord sans -i et relire la sortie
-i.bak garde une copie .bak pendant l'édition, pour revenir en arrière ; à prendre en habitude
-n supprime l'affichage automatique ; avec p, n'affiche que les lignes demandées
-E expressions étendues (+ ? | ( ) sans échappement) ; sur les vieux systèmes c'est -r, même effet
-e enchaîne les expressions ; on peut aussi les séparer par des points-virgules
Le g de s/ancien/nouveau/g remplace toutes les occurrences d'une ligne ; sans g, la première seulement
Plages d'adresses : '/^server/,/}/ s/a/b/' ne modifie qu'un bloc de configuration
\1 \2 référencent les groupes de capture ; & représente la correspondance entière
L'ordre compte : sed -E -i.bak 's/x/y/' FICHIER
sed -n '1,20p' /etc/nginx/nginx.conf # afficher seulement les 20 premières lignes sed -i.bak 's/listen 80;/listen 8080;/' /etc/nginx/sites-enabled/app sed -E -i.bak 's/^server_name[[:space:]]+([^;]+);/server_name \1.bak;/' app.conf sed '/^[[:space:]]*#/d;/^[[:space:]]*$/d' app.conf | head # supprimer les commentaires et les lignes vides
-F fixe le séparateur : -F: deux-points, -F'\t' tabulation, par défaut les suites d'espaces
'{print $1,$3}' prend les colonnes 1 et 3 ; $0 la ligne entière ; NF le nombre de champs ; NR le numéro de ligne
Les filtres précèdent le programme : '$9==500' signifie colonne 9 égale 500
Forcer la comparaison numérique avec +0 : $9+0 >= 500, sinon la comparaison de chaînes surprend
'{s+=$7} END {print s}' est le modèle de somme ; pour compter, '{c++} END {print c}'
Statistiques par tableau : {c[$1]++} END {for (k in c) print k, c[k]}
-v passe une variable : awk -v lim=500 '$3>lim' — -v avant le programme
-F et -v sont analysés par awk lui-même, donc avant le programme entre guillemets
Dans les exemples : sort -rn trie en numérique inverse, uniq -c compte, ss -tn liste les connexions TCP
awk -F: '$3>=1000 {print $1, $3}' /etc/passwd
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
awk -v lim=500 '{ if ($9+0 >= lim) print NR": "$7 }' access.log
ss -tn | awk 'NR>1 {c[$1]++} END {for (k in c) print k, c[k]}'sort -n ordre numérique (par défaut, l'ordre lexical met 10 avant 9)
sort -h comprend 1K/2M/3G — indispensable sur la sortie de du et df
sort -r inverse ; -u trie et dédoublonne en une passe
-k2 trie sur le champ 2, -k3 de même ; plusieurs clés avec -k1,1 -k2,2
-t: fixe le séparateur à deux-points (par défaut : suites d'espaces)
uniq -c compte les répétitions consécutives — d'où le sort préalable, sinon les valeurs égales sont éclatées en blocs
uniq -d n'affiche que les doublons, uniq -u que les lignes uniques
Le schéma Top N : sort | uniq -c | sort -rn | head
Dans l'exemple du -sh : -s sort un total au lieu d'une ligne par entrée (voir du / df)
du -sh /var/log/* | sort -h | tail -5
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
sort -t: -k3 -n /etc/passwd | headcut -d: -f1,7 prend des champs par séparateur : -d le fixe, -f choisit les champs (plusieurs possibles)
cut -c1-19 découpe par position — utile pour l'horodatage d'un journal à largeur fixe
cut ne connaît qu'un séparateur ; une sortie alignée par espaces le casse, passer à awk
tr 'a-z' 'A-Z' mappe caractère par caractère ; pour remplacer une chaîne, utiliser sed
tr -d supprime des caractères — le plus souvent tr -d '\r' pour les fins de ligne Windows
tr -s comprime les répétitions en un seul caractère, pratique pour les suites d'espaces
tr ne lit que l'entrée standard, pas les fichiers : rediriger avec <
Dans les exemples : df -h lisible, cut -f5 prend le champ 5, sort -u dédoublonne
cut -d: -f1,7 /etc/passwd | head cut -c1-19 /var/log/nginx/access.log | head -3 tr -d '\r' < crlf.txt > lf.txt df -h | tr -s ' ' | cut -d' ' -f5 | sort -u
xargs transforme chaque ligne de l'entrée standard en argument — c'est le principe de find | xargs rm
-0 s'utilise avec find -print0 : un nom avec espace ou saut de ligne n'est plus découpé (obligatoire pour les fichiers déposés)
find -print0 termine chaque résultat par \0, exactement ce qu'attend xargs -0
-n1 passe un argument par invocation : plus sûr, et on voit lequel a échoué
-I {} est un espace réservé, l'argument peut se placer au milieu : xargs -I {} cp {} /backup
-p demande avant d'exécuter ; -t affiche la commande d'abord — -t quand on ignore ce qui sera construit
-r n'exécute rien sur une entrée vide (extension GNU), sinon une liste vide déclenche quand même un rm
-P4 lance quatre processus en parallèle — plusieurs fois plus rapide en compression ou téléchargement par lot
cp --parents recrée l'arborescence, utile pour restaurer une sauvegarde
Dans les exemples : find -name / -mtime / -type filtrent, et rm -f force sans demander
find /var/log -name '*.log.*' -mtime +7 -print0 | xargs -0 -r rm -f
find /srv/uploads -type f -print0 | xargs -0 -I {} cp --parents {} /backup
printf '%s\n' a b c | xargs -n1 -t echotee écrit son entrée à la fois dans un fichier et sur la sortie standard — l'outil pour garder une trace
-a ajoute au lieu d'écraser ; à utiliser systématiquement pour les journaux
Pourquoi sudo tee : dans sudo echo x > /etc/f la redirection est faite par le shell, donc elle échoue
-i ignore les interruptions, une longue capture n'est pas coupée par Ctrl-C
Fusionner stderr avec 2>&1 avant le tube, sinon les erreurs n'atteignent jamais tee
Horodater un flux avec ts, ou : awk '{print strftime("%F %T"), $0}'
./deploy.sh 2>&1 | tee -a /var/log/deploy-$(date +%F).log echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
jq lit le JSON par structure, pas par correspondance de lignes : ce que grep ne fait pas sûrement
. lit les champs : .data .items[0].name ; .[] parcourt les tableaux
-r sort les chaînes brutes sans guillemets — requis pour alimenter une variable ou un autre tube
-c met chaque résultat sur une ligne, ce qui facilite le comptage par sort / uniq
'keys' liste les champs d'un objet ; 'length' la taille d'un tableau ; '..' tous les chemins quand la forme est inconnue
select() filtre : '.[] | select(.status != "ok")'
-C colore pour un humain, -M force la sortie brute pour les fichiers et scripts
jq formate par défaut ; -e transforme le résultat en code de sortie pour les scripts
Dans les exemples curl -s coupe la progression, qui corromprait le JSON
curl -s https://api.example.com/v1/tools | jq -r '.data[].name'
curl -s 'https://api.example.com/v1/list?page=1' | jq -c '.items[] | {id, name}'
jq -r '.[] | select(.status != "ok") | .id' resp.jsondiff -u sortie unifiée, format patch, directement applicable par patch
diff -r compare deux arborescences ; -q indique seulement quels fichiers diffèrent
diff -y affiche côte à côte, -W 200 fixe la largeur — pratique pour comparer des configurations à l'œil
wc -l compte les lignes (la métrique de journal habituelle) ; -c les octets ; -w les mots
wc affiche aussi le nom du fichier : en script, récupérer avec awk '{print $1}'
comm est plus adapté aux différences entre deux listes triées
comm -13 a b affiche les lignes présentes seulement dans b ; vérifier la logique des colonnes avant de s'y fier
Trier et dédoublonner les deux côtés d'abord, sinon « tout est différent » n'est qu'un problème d'ordre
diff -u /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf | head -40 diff -rq /srv/app/releases/20261028 /srv/app/releases/20261029 wc -l /var/log/nginx/access.log sort a.txt > a.s; sort b.txt > b.s; comm -13 a.s b.s # lignes présentes uniquement dans b
Connexion, clés, transfert, reprise — le plus long dans une journée est souvent d'amener le fichier là-bas
ssh utilisateur@hote est la connexion de base ; port 22 par défaut
-p 2222 fixe le port (attention : scp et sftp utilisent -P majuscule, ne pas les confondre)
-i pointe vers une clé privée ; sinon ~/.ssh/id_ed25519 et id_rsa sont essayés
-t force un pseudo-terminal — requis pour sudo, top et autres outils interactifs à distance
-v trace la poignée de main et l'authentification ; indique si le blocage vient du réseau ou de l'auth ; -vvv pour plus de détail
-o passe des options : -o ConnectTimeout=5 évite les blocages, -o BatchMode=yes interdit toute interaction (obligatoire en script)
Exécuter une seule commande distante : ssh HOTE 'cmd' — la base des vérifications par lot
-J passe par un bastion : ssh -J bastion@1.2.3.4 deploy@10.0.0.5 (ancienne forme : -o ProxyCommand)
Dans les exemples df -h signifie lisible (voir du / df)
ssh -p 2222 -i ~/.ssh/id_ed25519 deploy@10.0.0.5 ssh -o ConnectTimeout=5 -o BatchMode=yes 10.0.0.5 'uptime; df -h /' ssh -v -t 10.0.0.5 'sudo systemctl restart app' ssh -J bastion@1.2.3.4 deploy@10.0.0.5
ssh-keygen -t ed25519 crée une paire de clés (plus courte et plus rapide que rsa, prise en charge partout)
-t choisit le type : ed25519 de préférence, rsa seulement sur vieux systèmes, avec -b 4096 pour la longueur
-C écrit un commentaire (usage ou adresse) pour distinguer plusieurs clés
-f nomme le fichier de sortie, évitant un écrasement qui casse silencieusement les connexions existantes
-N '' fixe une phrase vide pour l'automatisation ; en interactif, Entrée suffit
ssh-copy-id ajoute la clé publique au ~/.ssh/authorized_keys distant et corrige les droits
-i (de ssh-copy-id) choisit la clé publique à envoyer ; par défaut id_*.pub
-R supprime une empreinte obsolète de known_hosts — à lancer après toute réinstallation, sinon REMOTE HOST IDENTIFICATION HAS CHANGED
-lf affiche l'empreinte et le type, pour vérifier que deux machines portent la même clé
Les clés privées doivent être en 600 et le répertoire en 700, sinon ssh refuse de les utiliser
ssh-keygen -t ed25519 -C 'deploy@icetools' -f ~/.ssh/id_deploy -N '' ssh-copy-id -i ~/.ssh/id_deploy.pub deploy@10.0.0.5 ssh-keygen -R 10.0.0.5 ssh-keygen -lf ~/.ssh/id_deploy.pub
Après ça, ssh app équivaut à ssh -p 2222 deploy@10.0.0.5 — plus rien à retaper
Host est l'alias et la portée des réglages qui suivent
HostName adresse réelle ; User compte de connexion ; Port port ; IdentityFile chemin de la clé
IdentitiesOnly yes ne propose que cette clé, au lieu de laisser l'agent en essayer une dizaine (trop de tentatives peuvent bloquer le compte)
ProxyJump désigne le bastion, équivalent de -J, et scp / rsync en héritent automatiquement
ServerAliveInterval 30 envoie un battement toutes les 30 s ; NAT et répartiteurs cloud coupent les connexions inactives
ServerAliveCountMax 3 coupe après trois battements perdus, en complément de l'intervalle
Host * en fin de fichier porte les valeurs par défaut (battement, délais)
Droits : config en 600 et ~/.ssh en 700, sinon Bad owner or permissions apparaît
# ~/.ssh/config (chmod 600)
Host app
HostName 10.0.0.5
User deploy
Port 2222
IdentityFile ~/.ssh/id_deploy
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 3
Host db
HostName 10.0.0.6
ProxyJump app
Host *
ServerAliveInterval 30-L transfert local : ssh -L 8080:127.0.0.1:80 HOTE mappe le port 80 distant sur 8080 local
-R transfert inverse : expose un port local côté distant, pour joindre de l'extérieur un service interne
-D transfert dynamique, crée un proxy SOCKS (à utiliser avec curl --socks5 ou un navigateur)
-N transfère seulement, sans shell ; -f passe en arrière-plan — les deux vont presque toujours ensemble
-g autorise d'autres machines à utiliser le port transféré (par défaut, écoute sur 127.0.0.1 seulement)
L'adresse intermédiaire de -L est résolue côté distant ; 127.0.0.1 y désigne l'hôte distant lui-même
Cas « la base n'est pas joignable directement » : amener le port en local avec -L, puis se connecter à 127.0.0.1
Port local occupé : en prendre un autre ; lsof -i :8080 montre le détenteur
ssh -N -f -L 15432:127.0.0.1:5432 deploy@10.0.0.5 ssh -N -L 8080:10.0.0.6:80 app ssh -D 1080 -N -f app ssh -g -L 8080:127.0.0.1:80 app
scp SRC DST — le sens décide : local en premier = envoi, distant en premier = récupération
-P majuscule fixe le port (ssh utilise -p minuscule ; c'est la confusion classique)
-r descend dans les répertoires ; sans lui, copier un répertoire échoue
-i choisit une clé privée ; -o passe des options ssh comme -o ConnectTimeout=5
-C compresse pendant le transfert — net sur du texte et des journaux, inutile sur une archive déjà compressée
-l 8000 limite le débit en Kbit/s (8000 ≈ 1MB/s) pour ne pas saturer la bande passante
-p minuscule conserve dates et droits — sans rapport avec -P, ne pas confondre
scp ne reprend pas un transfert interrompu ; au-delà de quelques Go, utiliser rsync -P
scp -P 2222 app.tar.gz deploy@10.0.0.5:/srv/ scp -r -i ~/.ssh/id_deploy ./dist deploy@10.0.0.5:/srv/app/ scp -C -l 8000 backup.tar.gz deploy@10.0.0.5:/backup/ scp -p deploy@10.0.0.5:/var/log/app/error.log ./
sftp utilisateur@hote ouvre une session interactive — « lister en transférant » vaut mieux qu'un scp à l'aveugle
-P majuscule pour le port et -i pour la clé, comme scp
get DISTANT récupère, put LOCAL envoie ; get -r prend un répertoire entier
reget reprend un téléchargement, reput un envoi — le vrai avantage sur scp
-p (de get/put) conserve droits et horodatages
mget / mput acceptent les jokers, mais demandent confirmation par fichier : lancer prompt off avant
ls / lls listent le distant et le local ; cd / lcd changent de répertoire (les commandes en l sont locales)
!df -h exécute une commande shell locale — vérifier l'espace avant de récupérer un gros fichier
bye / exit quitte ; un script en heredoc rend les transferts par lot non interactifs
sftp -P 2222 -i ~/.ssh/id_deploy deploy@10.0.0.5 <<'EOF' get -r /srv/app/logs ./ reget backup.tar.gz put ./dist/app.tar.gz /srv/ EOF
-a mode archive (= -rlptgoD) : conserve droits, dates, liens symboliques, groupe — le choix par défaut
-v affiche la progression ; -z compresse en transit, indispensable sur Internet
-P équivaut à --partial --progress : garder le fichier partiel et afficher la progression — c'est l'interrupteur de reprise
--partial conserve ce qui est arrivé pour reprendre plus tard ; sans lui chaque exécution repart de zéro
--append ajoute seulement, adapté aux journaux qui ne font que croître, pas aux fichiers modifiés
--delete aligne la destination sur la source (supprime les extras) — répéter d'abord avec -n
-n et --dry-run sont équivalents : afficher ce qui serait fait, sans rien changer
-e 'ssh -p 2222' choisit le shell distant et le port ; rsync n'a pas d'option de port à lui
--exclude retire un motif, --exclude-from lit une liste depuis un fichier
--bwlimit=2000 limite en KB/s ; --checksum compare le contenu plutôt que taille et date
--info=progress2 affiche une progression globale, préférable au -P par fichier sur de grosses arborescences
Le piège principal : une barre oblique finale sur la source copie le CONTENU, sans barre copie le répertoire
rsync -avzP ./dist/ deploy@10.0.0.5:/srv/app/dist/ rsync -avzP -e 'ssh -p 2222' deploy@10.0.0.5:/var/log/app/ ./logs/ rsync -avn --delete /srv/data/ /backup/data/ # simulation, bien vérifier avant l'exécution réelle rsync -avz --bwlimit=2000 --exclude='*.tmp' ./ /mnt/nas/app/
wget URL télécharge dans le répertoire courant ; -O nomme la sortie, -O - écrit sur la sortie standard
-c reprend (curl exige -C -) ; relancer la même commande après une coupure suffit
-q mode silencieux ; --progress=dot:giga donne une progression compacte
--limit-rate=1m limite le débit, suffixes k/m ; --tries=3 fixe le nombre d'essais
-T 30 fixe le délai de connexion en secondes ; --timeout en est la forme longue
-r met un site en miroir, -np empêche de remonter aux répertoires parents (et d'aspirer tout le domaine)
--no-check-certificate ignore la vérification du certificat — diagnostic interne seulement, jamais dans un script
Pour des en-têtes, des cookies ou une session, curl est plus souple
wget -c https://example.com/big.iso wget -O - https://example.com/install.sh | head -40 # examiner le contenu avant de décider de l'exécuter wget --limit-rate=1m --tries=3 -c -O app.tar.gz https://example.com/app.tar.gz
Modes numériques, bits spéciaux, sudo — un droit mal réglé se manifeste surtout par « mot de passe correct mais accès refusé »
Numérique : 4 lecture / 2 écriture / 1 exécution, les trois chiffres sont propriétaire / groupe / autres
755 pour les répertoires (rwxr-xr-x) ; 644 pour les fichiers (rw-r--r--)
600 lecture-écriture du propriétaire seul — obligatoire pour les clés privées
Symbolique : u/g/o/a pour qui, +/-/= pour quoi, X n'ajoute l'exécution qu'aux répertoires
-R récursif ; préférer a+X à a+x pour ne pas rendre exécutables les fichiers simples
go-w retire l'écriture au groupe et aux autres, dans la même passe que a+X
Après un changement récursif, vérifier un fichier et un répertoire avec ls -l
chmod 600 ~/.ssh/id_rsa chmod -R a+X,go-w /srv/app/public ls -l /srv/app/public | head # vérifier par échantillon après la modification récursive
chown utilisateur:groupe chemin ; un point fonctionne aussi comme séparateur
Changer seulement le groupe avec chown :groupe, ou chgrp
-R récursif ; ajouter -h pour toucher le lien symbolique et non sa cible
--reference=FICHIER copie propriétaire et droits depuis un fichier existant
Seul root change le propriétaire ; un utilisateur ne change que le groupe de ses propres fichiers
Permission denied sur un volume de conteneur : comparer l'uid du répertoire hôte avec l'utilisateur du conteneur
chown -R www-data:www-data /var/www/app chown --reference=/srv/app/a /srv/app/b
4000 SUID : s'exécute en tant que propriétaire du fichier (c'est le cas de /usr/bin/passwd)
2000 SGID : s'exécute avec le groupe ; sur un répertoire, les nouveaux fichiers héritent de son groupe
1000 sticky : seul le propriétaire peut supprimer dans le répertoire (c'est le cas de /tmp)
Un s ou t dans ls signifie le bit correspondant ; S/T majuscule signifie « posé sans x »
-perm -4000 signifie « droits contenant SUID » ; -perm est le test de droits de find
-xdev reste sur un système de fichiers — sans lui, find parcourt NFS et montages de conteneurs et rampe
-ls affiche les résultats comme ls -dils, taille et date comprises
-type f se limite aux fichiers simples, sans répertoires ni liens symboliques
Audit d'élévation de privilèges : find / -xdev -perm -4000 -type f
# audit : lister tous les programmes SUID find / -xdev -perm -4000 -type f -ls 2>/dev/null # répertoire partagé : collaboration dans le groupe + suppression réservée au propriétaire chmod 2770 /srv/share
umask est l'ensemble des bits à RETIRER, pas les droits eux-mêmes
022 donne 755 aux répertoires et 644 aux fichiers (un fichier n'a jamais x par défaut)
077 donne 700 aux répertoires et 600 aux fichiers — adapté aux clés
-S affiche les droits par défaut résultants en symbolique (rwxr-xr-x), plus lisibles que les chiffres
Temporaire : umask 077 n'agit que sur le shell courant
Persistant : à ajouter dans /etc/profile ou ~/.bashrc
Si des clés nouvellement créées sont lisibles par tous, vérifier umask d'abord
umask # afficher la valeur actuelle umask 077 # valable pour la session courante umask -S # afficher les permissions par défaut réelles en notation symbolique
sudo -i ouvre un shell root de connexion neuf (avec l'environnement de root)
sudo -s shell root avec l'environnement et le PATH courants
sudo -u UTILISATEUR exécute en tant qu'un autre utilisateur — courant pour tester un service
sudo -l liste ce qui est autorisé — premier réflexe quand une commande est refusée
su - passe à root avec son environnement ; sans le tiret, l'environnement courant est conservé
Éditer sudoers avec visudo ; -c valide la syntaxe sans enregistrer — à lancer après chaque édition
Éditer /etc/sudoers à la main peut verrouiller l'accès — la panne auto-infligée classique
sudo -l sudo -u www-data /usr/bin/env # voir l'environnement du compte de service visudo -c # contrôle de syntaxe uniquement
useradd -m crée un répertoire personnel ; -s fixe le shell ; -G ajoute des groupes secondaires
Sans -m, pas de répertoire personnel et beaucoup d'outils échouent de façon déroutante
usermod -aG GROUPE UTILISATEUR — sans -a, l'utilisateur est retiré de ses autres groupes (-a pour append)
-G prend la liste des groupes et se combine en -aG
id UTILISATEUR affiche uid/gid et tous les groupes ; groups UTILISATEUR seulement les groupes
userdel -r supprime aussi le répertoire personnel ; sans -r, des fichiers orphelins restent
Les changements de groupe ne s'appliquent pas aux sessions ouvertes — se reconnecter
useradd -m -s /bin/bash deploy usermod -aG docker deploy id deploy
Qui consomme du CPU, qui tient un fichier, comment arrêter proprement — l'erreur classique ici est de sortir kill -9 trop tôt
ps aux est le style BSD : %CPU, %MEM, STAT, COMMAND — le choix quotidien
ps -ef est le style System V : UID, PID, PPID, C, STIME, CMD — montre les PID parents
-e sélectionne tous les processus (style System V) ; -o choisit les colonnes
ps -aux fonctionne mais signifie autre chose ; à ne pas ériger en habitude
--sort=-%cpu trie par CPU décroissant (le moins veut dire décroissant) ; les virgules enchaînent les clés
--sort=-rss trie par mémoire réelle ; dans STAT, Z est zombie, D ininterruptible, R en cours
RSS est l'occupation réelle ; VSZ est l'espace d'adressage et n'a guère de sens
ps aux --sort=-%cpu | head ps -eo pid,ppid,rss,pcpu,cmd --sort=-rss | head
Dans top, P trie par CPU, M par mémoire, T par temps cumulé
1 déploie le détail par cœur — distingue la saturation d'un cœur d'une charge globale
c affiche la ligne de commande complète au lieu de la tronquer
H bascule les threads ; u filtre par utilisateur ; k tue
-b mode batch et -n1 n'affiche qu'un instantané (-n est le nombre d'itérations) — utile en script
-p se limite aux PID donnés, séparés par des virgules ; le -d de pgrep joint les PID par des virgules
Le load average porte sur 1/5/15 minutes ; au-delà du nombre de cœurs, les requêtes font la queue
top -b -n1 | head -15 # prendre un instantané top -p $(pgrep -d, nginx) # ne voir qu'un processus donné
kill envoie TERM(15) par défaut — le processus peut vider ses tampons et sortir proprement
-15 équivaut à -TERM ; la forme nommée est plus lisible, et c'est celle à taper d'abord
-9 est KILL : le noyau récupère immédiatement, sans nettoyage — bases de données et files peuvent rester corrompues
-1 ou -HUP sert couramment à recharger la configuration (nginx, sshd)
-0 n'envoie rien et vérifie l'existence ; le code de sortie est la réponse
kill prend des PID, pas des noms ; pour les noms, pkill ou killall
pkill -HUP nginx signale tous les processus nginx par leur nom
Toujours TERM, attendre quelques secondes, puis KILL
kill -TERM 1234 && sleep 3 kill -0 1234 && kill -9 1234 pkill -HUP nginx # recharger la configuration
& ne fait que passer en arrière-plan ; fermer le terminal envoie SIGHUP et le processus meurt
nohup ignore SIGHUP et écrit dans nohup.out par défaut (rediriger explicitement)
2>&1 fusionne stderr dans stdout, sinon les erreurs disparaissent sans laisser de trace
< /dev/null branche l'entrée standard sur le périphérique nul, le job ne peut plus bloquer sur le terminal
setsid le détache complètement de la session — plus fort que nohup
disown retire un job d'arrière-plan existant de la table du shell
Pour tout ce qui dure, utiliser systemd ; nohup ne sert qu'à vérifier vite
nohup ./run.sh > /var/log/run.log 2>&1 & setsid ./worker.sh < /dev/null > /var/log/worker.log 2>&1 &
lsof -i :8080 montre qui tient le port 8080 (-i filtre par adresse réseau)
-p PID liste tous les fichiers ouverts par un processus
-u UTILISATEUR filtre par utilisateur ; -n évite les résolutions de noms, plus rapide et sans blocage DNS
+D /chemin parcourt un répertoire récursivement (lent sur de grosses arborescences)
+L1 liste les fichiers dont le nombre de liens est inférieur à 1 — supprimés mais ouverts, cause classique d'espace non libéré
Un tube vers sort -k7 -n trie numériquement sur la colonne 7 (SIZE/OFF)
Sans lsof, utiliser fuser -v ou inspecter /proc/PID/fd
lsof -i :8080 lsof -n +L1 | sort -k7 -n | tail # voir les fichiers supprimés encore ouverts, triés par taille
nice va de -20 (priorité haute) à 19 (basse) ; un utilisateur non root ne peut qu'augmenter la valeur
nice -n 19 CMD lance une commande en basse priorité — adapté aux sauvegardes et compressions
-n porte la valeur nice elle-même, pas un compteur
renice -n 10 -p PID modifie un processus en cours ; -p nomme le PID, -u agit par utilisateur
taskset -c 0-3 épingle des cœurs : -c prend la liste de CPU, ce qui réduit la contention entre instances
Dans taskset -cp 0-3 1234, -p signifie « agir sur un processus existant » et le PID suit
Baisser la priorité ne crée pas de capacité — cela protège seulement la charge importante
Dans les exemples tar -czf : -c crée, -z compresse en gzip, -f nomme l'archive (voir tar)
nice -n 19 tar -czf backup.tar.gz /srv/data renice -n 10 -p 1234 taskset -cp 0-3 1234
watch relance la commande toutes les 2 secondes et efface l'écran — on voit le changement, pas un instantané
-n 1 rafraîchit chaque seconde ; -n prend les secondes (0.5 est accepté)
-d met en évidence ce qui a changé entre deux passes, pour repérer le mouvement immédiatement
-d=cumulative garde la marque sur tout ce qui a changé — utile pour les pics intermittents
-t masque la ligne d'en-tête (commande et intervalle), écran plus propre
Mettre la commande entre guillemets, sinon le shell découpe le tube et watch ne reçoit que la moitié
À éviter pour les métriques haute fréquence (connexions, QPS) ; vmstat 1 ou sar montrent ce que watch rate
Dans les exemples ss -s résume les connexions et wc -l compte les lignes (l pour lines)
watch -n 1 -d 'ss -tn state established | wc -l' watch -d=cumulative -t 'ss -s'
screen -S deploy ouvre une session nommée ; fermer le terminal ou perdre le réseau la laisse tourner
-list liste les sessions (aussi -ls) — vérifier d'abord une ancienne session en Attached
-r deploy rattache ; si Attached s'affiche, une autre session la tient — -d -r pour la reprendre
Ctrl-a d détache dans screen ; Ctrl-a " liste les fenêtres ; Ctrl-a k tue la fenêtre courante
tmux new -s deploy en crée une, tmux attach -t deploy rattache (-t la nomme)
Dans tmux, Ctrl-b d détache et Ctrl-b [ ouvre le mode copie pour remonter loin
Ouvrir une session avant migration, sauvegarde ou gros transfert — une coupure n'arrête pas le job, elle le cache
screen -S deploy # ensuite appuyer sur Ctrl-a d pour détacher screen -list screen -r deploy tmux new -s deploy
Quand un service ne répond pas, distinguer « n'écoute pas », « pas autorisé » et « injoignable » — les contrôler dans le désordre fait perdre beaucoup de temps
-t TCP ; -u UDP ; -l écoute seulement ; -n numérique (sans résolution de noms, plus rapide) ; -p montre le processus
-tulnp est la combinaison quotidienne — cinq lettres, cinq rôles
-tnp sans le l inclut le processus des connexions établies, le contrôle de comptage habituel
-a inclut les connexions établies ; -s résumé statistique — premier réflexe quand les connexions explosent
state established filtre par état, plus sûr qu'un grep ESTAB (ce n'est que l'abréviation de colonne)
netstat est obsolète et souvent absent des systèmes modernes ; utiliser ss
Écouter sur 127.0.0.1 ou 0.0.0.0 change tout : le premier est local seulement, première cause de « le port est ouvert mais injoignable »
ss -tulnp ss -tnp state established | head ss -s
-s silencieux : pas de barre de progression ni de message d'erreur — quasi toujours voulu en script
-S complète -s pour réafficher les erreurs — donc -sS signifie « silencieux, mais parler en cas d'échec »
-i inclut les en-têtes de réponse ; -I en-têtes seuls (envoie HEAD, sans corps)
-v trace complète de la poignée de main et de la requête — montre l'étape qui bloque
-L suit les redirections ; indispensable pour diagnostiquer une chaîne 301/302
-X fixe la méthode ; -d envoie un corps (POST par défaut) ; -H ajoute un en-tête
-o /dev/null jette le corps et s'utilise avec -w pour n'avoir que statut et durée
-w '%{http_code} %{time_total}' sortie personnalisée, plus rapide qu'un examen à l'œil
-m 10 borne la requête entière à 10 s ; --connect-timeout 3 ne limite que la connexion
--resolve hote:443:IP contourne le DNS en épinglant une IP — indique si le DNS est le problème
-x http://proxy:8080 passe par un proxy ; --noproxy '*' force la connexion directe (souvent nécessaire en test local)
-k ignore les vérifications de certificat ; diagnostic seulement, jamais dans un script
Dans -w, \n est un saut de ligne — sans lui tout s'écrase sur une ligne
curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://example.com/
curl -m 10 --connect-timeout 3 -sS -o /dev/null \
-w 'dns:%{time_namelookup} conn:%{time_connect} ttfb:%{time_starttransfer} total:%{time_total}\n' \
https://example.com/api
curl -iL --resolve example.com:443:1.2.3.4 https://example.com/
curl -v -X POST -H 'Content-Type: application/json' -d '{"a":1}' http://127.0.0.1:8080/apinc -zv HOTE PORT — -z sonde sans rien envoyer, -v affiche le verdict, l'option la plus rapide
-w3 fixe un délai de 3 secondes pour que la commande ne bloque pas (sans lui, attente longue)
telnet HOTE PORT existe sur les vieux systèmes ; « Connected » signifie joignable
Le /dev/tcp intégré à bash ne demande rien à installer : echo > /dev/tcp/HOTE/PORT
« refused » signifie hôte joignable mais rien en écoute ; un délai dépassé trahit un pare-feu ou un groupe de sécurité — deux sens très différents
Toujours resserrer par couches : ss local, puis curl 127.0.0.1, puis nc depuis le même sous-réseau, puis depuis l'extérieur
nc -zv -w3 10.0.0.5 5432 echo > /dev/tcp/10.0.0.5/5432 && echo open || echo closed
ip a affiche toutes les adresses d'interface (l'ancien ifconfig) ; -4 IPv4 seul, -6 IPv6 seul
ip r affiche la table de routage ; la ligne default est la passerelle
ip neigh affiche la table ARP — à consulter quand le trafic dans le même sous-réseau échoue
ip -s link donne les compteurs de paquets et d'erreurs (-s pour statistics) ; errors/dropped non nuls = couche liaison
show eth0 restreint à une interface, sinon les interfaces virtuelles noient l'affichage
add / del changent les adresses temporairement, perdues au redémarrage
Persistance selon la distribution : netplan ou /etc/network/interfaces sur Debian, nmcli sur RHEL
ip -4 a ip r ip -s link show eth0
+short n'affiche que la réponse — adapté aux scripts
+A / +AAAA / +MX choisissent le type d'enregistrement (A par défaut)
@8.8.8.8 interroge un résolveur précis, pour comparer réponse locale et publique
+trace descend depuis la racine pour trouver la délégation fautive
En cas d'échec, lire status : NXDOMAIN signifie enregistrement absent, SERVFAIL un serveur en panne
Le TTL fixe le délai de propagation d'un changement ; le TTL restant dans ANSWER SECTION indique la fraîcheur du cache
dig ne couvre que le DNS. Si la résolution marche mais le trafic échoue, chercher au-delà : routage et ports
dig +short example.com dig @8.8.8.8 example.com A dig +trace example.com
-i any écoute toutes les interfaces (ou en nommer une) ; -n saute les résolutions de noms (plus rapide, pas de blocage DNS)
-nn impose IP et ports numériques, plus clair que -n seul en diagnostic
port 443 filtre par port ; host 1.2.3.4 par hôte ; combiner avec and / or
-c 100 s'arrête après 100 paquets au lieu d'inonder le terminal
-w fichier.pcap enregistre pour Wireshark, bien meilleur qu'une lecture dans le terminal
-A affiche la charge utile en ASCII, -X en hexadécimal plus ASCII (utile seulement pour les protocoles en clair)
tcp[tcpflags] & tcp-rst != 0 ne capture que les RST, pour savoir qui a fermé en premier
Nécessite root ; toujours ajouter -c et un filtre en production
tcpdump -i any -nn -c 50 'port 443 and host 1.2.3.4' tcpdump -i eth0 -nn -w /tmp/cap.pcap 'tcp[tcpflags] & tcp-rst != 0'
-connect HOTE:443 inspecte le certificat du pair au niveau TLS, en ignorant HTTP
-servername HOTE est obligatoire (SNI), sinon une IP multi-domaines renvoie le mauvais certificat
-showcerts imprime la chaîne complète, révélant un intermédiaire manquant
-noout supprime le certificat brut et n'imprime que ce qui est demandé
-dates affiche notBefore / notAfter — premier réflexe de toute enquête d'expiration
-subject indique à qui le certificat a été délivré ; -ext subjectAltName liste les noms couverts
-checkend 2592000 vérifie l'expiration sous 30 jours (en secondes) ; le code de sortie est exploitable en supervision
</dev/null fait sortir s_client après lecture, sinon il attend une entrée
openssl s_client -connect www.icetools.cn:443 -servername www.icetools.cn </dev/null 2>/dev/null \ | openssl x509 -noout -dates -subject openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null | head -30 openssl s_client -connect example.com:443 -servername example.com -checkend 2592000 </dev/null >/dev/null \ && echo '30 天内不过期'
firewall-cmd --list-all montre ce que la zone active autorise (famille RHEL)
--add-port=8080/tcp ajoute une règle ; seul --permanent survit à un rechargement
--reload applique les règles permanentes — obligatoire après un ajout, sinon elles n'entrent jamais en service
ufw status verbose affiche les règles (Ubuntu)
iptables -L liste la table filter ; -n garde les noms numériques, -v montre les compteurs prouvant qu'une règle s'applique
--line-numbers affiche les indices de règle, à citer pour iptables -D INPUT 3
-I INPUT 1 insère en tête quand -A ajoute en fin — l'ordre décide du résultat
Si l'accès échoue malgré la règle, suspecter le groupe de sécurité du fournisseur cloud ; il est hors de cette machine
firewall-cmd --list-all firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload iptables -L INPUT -n -v --line-numbers
« Disque plein » a quatre causes distinctes : beaucoup de fichiers, un fichier énorme, inodes épuisés, fichiers supprimés mais encore ouverts
Étape 1 : df -h pour trouver le point de montage plein — ne pas redémarrer le service tout de suite
Étape 2 : df -i pour les inodes. Une table pleine renvoie « no space left on device » alors que df -h montre de la place
Étape 3 : du -h --max-depth=1 pour descendre niveau par niveau vers le gros répertoire
Étape 4 : si les deux chiffres divergent, lsof +L1 trouve les fichiers supprimés mais ouverts ; seul un redémarrage ou un kill du processus les libère
La ligne awk additionne la colonne 7 (octets) et convertit en Mo — plus rapide qu'une estimation à l'œil
Une croissance de journaux se voit dès l'étape 1 — lire ce qui inonde avant de supprimer quoi que ce soit
df -h; df -i
du -h --max-depth=1 / | sort -h | tail
lsof +L1 2>/dev/null | awk '{s+=$7} END {print s/1024/1024" MB held"}'Arborescence par défaut, montrant comment LVM, partitions et points de montage s'imbriquent
-f affiche type de système de fichiers, UUID et point de montage — à vérifier avant d'éditer fstab
-p imprime les chemins complets, pour ne pas confondre /dev/sda et /dev/sda1
-d masque les sous-périphériques et ne montre que les disques entiers
-o choisit les colonnes ; NAME,SIZE,FSTYPE,MOUNTPOINT,UUID est le jeu utile
Après agrandissement, si lsblk montre la nouvelle taille et df non, le système de fichiers n'a pas été redimensionné
lsblk -f lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT,UUID
Agrandir un disque cloud prend deux étapes : la partition, puis le système de fichiers — en sauter une et df ne bouge pas
growpart /dev/vda 1 étend la partition (paquet cloud-utils-growpart) ; le second argument est le numéro de partition
xfs_growfs / agrandit XFS (seulement en croissance, pas de réduction)
resize2fs /dev/vda1 agrandit ext4 (réduction possible, mais après umount et vérification)
LVM exige lvextend en étape supplémentaire, puis le redimensionnement du système de fichiers
Confirmer avec df -h que la capacité a bien changé
Instantané d'abord — une coupure pendant le redimensionnement peut perdre des données
growpart /dev/vda 1 xfs_growfs / # XFS resize2fs /dev/vda1 # ext4 df -h /
Options -o courantes : ro lecture seule, remount, noatime pour réduire l'amplification d'écriture
remount est l'outil de base : changer les options sans démonter, donc sans coupure
Si umount répond device is busy, trouver le détenteur avec lsof +D ou fuser -vm
Préférer UUID à /dev/sdb dans fstab — les noms de périphériques changent
Le champ 4 vaut en général defaults,noatime ; les champs 5 et 6 sont dump et ordre fsck, 0 0 pour un disque de données
Toujours lancer mount -a après avoir édité fstab ; une ligne fautive peut empêcher le prochain démarrage
mount -a monte tout ce que fstab liste et qui ne l'est pas, en nommant la ligne fautive en cas d'erreur
mount -o remount,rw / fuser -vm /mnt/data mount -a # valider la syntaxe de fstab
Chaque fichier consomme un inode, et le total est fixé au formatage
Symptôme : df -h affiche de la place mais l'écriture échoue avec « No space left on device »
df -i affiche IUse% ; proche de 100 %, c'est confirmé
-xdev reste sur un système de fichiers, pour ne pas déborder sur les autres montages
-type f ne compte que les fichiers — les répertoires consomment aussi des inodes, mais la répartition des fichiers est plus parlante
Le sed retire tout après le dernier / pour ne garder que le répertoire ; sort | uniq -c compte les fichiers par répertoire
Coupables habituels : fichiers de session, fichiers temporaires d'envoi, spools mail, répertoires de cache à des centaines de milliers de petits fichiers
Corriger durablement demande un reformatage avec un ratio -i plus grand ; à court terme, supprimer les petits fichiers
Le sort -rn final signifie tri numérique inverse : -r inverse, -n compare en numérique
df -i find /var -xdev -type f | sed 's#/[^/]*$##' | sort | uniq -c | sort -rn | head
Une tâche cron cassée vient le plus souvent du PATH ou d'une redirection ; une horloge fausse casse certificats, JWT et journaux ensemble
crontab -l liste les tâches de l'utilisateur ; -e édite ; -r supprime tout, sans confirmation
-u UTILISATEUR agit sur la crontab d'un autre compte (à lancer en root)
Les cinq champs sont minute heure jour mois jour-semaine : 0 3 * * * signifie 03:00 chaque jour
*/5 toutes les 5 minutes ; 1-5 du lundi au vendredi ; 0,12 seulement à 00:00 et 12:00
L'environnement de cron n'est pas le shell de connexion et le PATH est minimal — toujours des chemins absolus
cron ne lit pas ~/.bashrc ; déclarer les variables en tête de crontab ou les sourcer dans le script
Rediriger la sortie : >> /var/log/x.log 2>&1, sinon les erreurs partent par mail et restent invisibles
Quand rien ne tourne : journalctl -u cron (Debian) ou /var/log/cron (RHEL)
Dans les exemples curl -sS est silencieux mais signale les erreurs (-s silence, -S erreurs), -o /dev/null jette le corps
crontab -e crontab -l # sauvegarde chaque jour à 3:30, sortie vers le journal 30 3 * * * /srv/app/backup.sh >> /var/log/backup.log 2>&1 # sonde de santé toutes les 5 minutes */5 * * * * /usr/bin/curl -sS -o /dev/null http://127.0.0.1:8080/health
Avantages réels : journaux dans journalctl, prochaine exécution visible, rattrapage des exécutions manquées
OnCalendar prend une syntaxe calendaire : *-*-* 03:30:00 chaque jour à 03:30 ; OnUnitActiveSec=5min toutes les 5 minutes
Persistent=true exécute une fois au démarrage si la machine était éteinte — cron ne sait pas le faire
Unit= nomme le service à lancer ; omis, le .service de même nom est utilisé
systemctl list-timers affiche tous les timers et leur prochain déclenchement
enable --now signifie « démarrer au boot et lancer maintenant » ; enable seul ne déclenche rien
Pour tester, démarrer le .service, pas le .timer — démarrer le timer n'exécute pas la tâche
La dernière ligne : journalctl -u choisit l'unité, -n 30 prend les 30 dernières lignes, --no-pager évite le pagineur
# /etc/systemd/system/backup.timer [Unit] Description=Daily backup [Timer] OnCalendar=*-*-* 03:30:00 Persistent=true [Install] WantedBy=timers.target # activation et vérification systemctl list-timers systemctl enable --now backup.timer journalctl -u backup -n 30 --no-pager
date affiche l'heure et le fuseau ; +%F donne 2026-10-29, +%T l'heure, '+%F %T' le duo usuel
-d '-1 hour' calcule il y a une heure, -d 'next monday' marche aussi — toute fenêtre temporelle en script passe par -d
-s fixe l'horloge directement ; jamais en production, cela brouille l'ordre des journaux et les contrôles de certificat
timedatectl affiche le fuseau et l'état de synchro NTP, plus complet que date
set-timezone Asia/Shanghai corrige le problème local le plus courant : rester en UTC
set-ntp true active la synchro automatique ; dans un conteneur, l'heure vient de l'hôte
Une horloge fausse se voit concrètement : certificats « not yet valid », JWT expirés aussitôt, horodatages incohérents, tâches déclenchées à contretemps
date '+%F %T' date -d '-1 hour' '+%F %T' timedatectl timedatectl set-timezone Asia/Shanghai
chronyc tracking montre le décalage et le stratum ; la ligne System time dit littéralement l'écart réel
Au-delà de quelques centaines de millisecondes il faut chercher : source injoignable, UDP 123 bloqué, dérive d'horloge de VM
chronyc sources -v liste chaque source, -v imprimant le détail (délai, gigue, sélection)
Un ^* en tête marque la source utilisée ; ^? signifie injoignable
chronyc makestep recale l'horloge immédiatement — à la main uniquement si le décalage est important
ntpq -p est l'équivalent pour les vieux systèmes ntpd, -p imprimant la liste des pairs
Sur un serveur cloud, préférer le NTP interne du fournisseur ; le NTP public est souvent limité ou refusé
chronyc tracking chronyc sources -v chronyc makestep ntpq -p # anciens systèmes (ntpd)
L'ordre compte plus que la syntaxe : borner la fenêtre temporelle, filtrer par mot-clé, puis lire le contexte
-u UNITE filtre sur un service
-f suit ; --since '10 min ago' ou --since '2026-10-29 09:00' borne la fenêtre
-p err affiche error et au-dessus ; les priorités vont de emerg à debug
-n 100 les 100 dernières lignes ; -r inverse (les plus récentes d'abord)
--no-pager évite le pagineur, pratique en redirection ou en tube
-o short-iso inclut le décalage de fuseau — indispensable entre régions
--disk-usage indique l'espace occupé par le journal ; --vacuum-time=7d ne garde que 7 jours
--vacuum-size=500M élague par volume — choisir l'un des deux
journalctl -u nginx -p err --since '30 min ago' --no-pager journalctl -u app -f -o short-iso journalctl --disk-usage
-f suit le descripteur de fichier ; après une rotation il continue sur l'ancien fichier et le journal semble figé
-F suit par nom et rouvre après rotation — à utiliser dès qu'on surveille un moment
-n 100 affiche d'abord les 100 dernières lignes ; -n0 n'affiche aucun historique, seulement le nouveau
-Fn0 est simplement -F plus -n0, le duo habituel pour un flux temps réel
--pid=PID arrête le suivi quand ce processus se termine
grep --line-buffered force grep à vider par ligne ; sans lui le tube bloque et rien ne s'affiche
Ensemble : tail -Fn0 app.log | grep --line-buffered -i error
Plusieurs fichiers : tail -F a.log b.log affiche des séparateurs ==> ; ne pas surveiller un seul fichier
tail -F /var/log/app/error.log tail -Fn0 /var/log/nginx/access.log | grep --line-buffered -i 500
Les OOM, erreurs IO disque, link down et pannes de pilote apparaissent ici
-T affiche des horodatages lisibles ; -w suit en continu
-l err,warn filtre par niveau (-l prend une liste séparée par virgules)
Le tampon circulaire est écrasé — récupérer tôt ; journalctl -k est la vue persistante des mêmes données
« Out of memory: Killed process » signifie que l'OOM killer a frappé, pas un plantage
Avec grep -iE : -i ignore la casse et -E active les expressions étendues (pour que | fonctionne, voir grep)
dmesg -T -l err,warn | tail -30 dmesg -T | grep -iE 'oom|killed process'
Confirmer avec dmesg -T | grep -i -A2 'killed process' — -A2 ajoute les deux lignes suivantes, qui portent l'empreinte mémoire de la victime
journalctl -k contient le même événement plus l'état mémoire du moment
status=9/KILL ou signal=KILL dans systemctl status signifie presque toujours l'OOM killer
--no-pager empêche le pagineur de prendre la main, utile en script et en capture d'écran
Ce n'est pas un plantage : le journal applicatif s'arrête net, sans pile d'appels
Trouver la cause : free -h pour la mémoire disponible, puis trier les processus par RSS
Dans un conteneur, vérifier la limite cgroup dans /sys/fs/cgroup/memory.max
dmesg -T | grep -i -A2 'killed process' systemctl status app --no-pager | head -20 free -h
Placer les configurations dans /etc/logrotate.d/, un fichier par service
daily / weekly pour la période ; rotate 7 conserve 7 archives
compress compresse les anciens journaux ; delaycompress laisse le plus récent non compressé
copytruncate copie puis tronque — pour les programmes incapables de rouvrir leurs fichiers, au prix d'une petite fenêtre de perte
postrotate signale au processus de rouvrir, plus propre que copytruncate
-d est une simulation ; toujours simuler après édition pour voir le plan
-f force une rotation immédiate, pour valider la configuration sans attendre la période
missingok tait « no such file », notifempty saute les journaux vides — les deux figurent dans presque toute configuration
# /etc/logrotate.d/app
/var/log/app/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
postrotate
systemctl reload app > /dev/null 2>&1 || true
endscript
}
# simulation d'abord, puis exécution forcée pour valider
logrotate -d /etc/logrotate.d/app
logrotate -f /etc/logrotate.d/appUn service qui ne démarre pas relève presque toujours de trois cas : mauvais chemin, mauvais utilisateur, dépendance absente — status le dit
start / stop / restart / reload — reload relit la configuration sans redémarrer, si le programme le permet
status montre les dernières lignes de journal et le code de sortie ; --no-pager évite le pagineur
enable pour le démarrage au boot ; --now le lance en même temps
disable retire l'autostart sans arrêter ; utiliser stop séparément
list-units --failed montre ce qui est cassé ; --all inclut les unités inactives
Les codes de sortie de is-active / is-enabled s'utilisent directement en script
cat affiche l'unité telle que systemd la lit (drop-ins compris) — à consulter après édition
systemctl status app --no-pager systemctl enable --now app systemctl list-units --failed
Placer dans /etc/systemd/system/NOM.service et toujours lancer daemon-reload ensuite
ExecStart exige un chemin absolu et le programme ne doit pas forker (sinon Type=forking)
Restart=on-failure avec RestartSec=5 évite les rafales de redémarrage
User / Group décident des droits ; sortir de root implique souvent de corriger le répertoire personnel et les propriétaires
EnvironmentFile garde les variables dans un fichier séparé au lieu de les mettre dans ExecStart
WorkingDirectory influe sur les chemins relatifs ; beaucoup de programmes ne trouvent pas leur configuration sans lui
StandardOutput=journal route les journaux vers journalctl
After ordonne seulement le démarrage et ne garantit rien ; pour le réseau, ajouter Wants=network-online.target
# /etc/systemd/system/app.service [Unit] Description=App After=network-online.target Wants=network-online.target [Service] Type=simple User=deploy WorkingDirectory=/srv/app EnvironmentFile=/etc/app/env ExecStart=/srv/app/bin/app Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
1. systemctl status : vérifier Active et les dernières erreurs — cela résout la plupart des cas
2. journalctl -u SERVICE -n 50 --no-pager pour la trace complète (-n est le nombre de lignes)
3. Confirmer que le chemin ExecStart existe et est exécutable : ls -l dessus
4. Lancer à la main sous l'utilisateur du service avec sudo -u — la vraie erreur apparaît presque aussitôt
5. Vérifier la politique : ausearch -m avc -ts recent (-m sélectionne le type avc, -ts l'heure de début)
Les refus AppArmor atterrissent dans dmesg ; dans un conteneur, lire aussi docker logs
status=203/EXEC signale un chemin ou des droits ; status=1 est une sortie du programme avec le code 1
systemctl status app --no-pager journalctl -u app -n 50 --no-pager sudo -u deploy /srv/app/bin/app # lancer à la main pour voir la vraie erreur ausearch -m avc -ts recent # traces de refus SELinux
Après édition d'un fichier .service : daemon-reload obligatoire, sinon systemd garde l'ancienne définition
Seules des valeurs changées dans EnvironmentFile : pas de reload, mais un restart est nécessaire pour les prendre en compte
Nouvelle unité : reload avant de pouvoir l'activer
Un service déjà démarré n'adopte pas la nouvelle configuration au reload — le redémarrer
Symptôme classique d'un reload oublié : ExecStart modifié mais status affiche encore l'ancienne commande
cat montre le fichier tel que systemd le voit, y compris les surcharges drop-in
systemctl daemon-reload && systemctl restart app systemctl cat app # vérifier ce que systemd voit réellement de ce fichier
After ordonne seulement le démarrage, sans garantir la réussite
Requires est une dépendance dure : si l'autre unité échoue, celle-ci aussi
Wants est souple : l'unité démarre quand même — le bon défaut dans la plupart des cas
Côté réseau, network.target signifie seulement que la pile est initialisée ; les adresses peuvent manquer
After=network-online.target n'attend réellement qu'avec Wants=network-online.target
« La résolution de noms a échoué » juste après le boot vient presque toujours de là
Vérifier avec systemctl list-dependencies SERVICE pour l'arbre
[Unit] Wants=network-online.target After=network-online.target # vérifier systemctl list-dependencies app
Vérifier la provenance avant d'installer, et ce qui part avec avant de supprimer — surtout en production
apt update ne rafraîchit que les index et n'installe rien — la paire la plus souvent inversée
apt upgrade met à jour les paquets installés ; full-upgrade peut en supprimer pour satisfaire les dépendances
install -y évite la confirmation, ce dont les scripts ont besoin ; sans -y, apt attend une saisie
apt purge supprime aussi les fichiers de configuration ; remove seul les laisse
apt list --installed montre l'existant ; apt list --upgradable filtre les mises à jour possibles
apt-cache show PAQUET donne version et description
apt-mark hold épingle une version pour que les montées en masse l'ignorent
apt update && apt install -y nginx apt list --upgradable apt purge nginx-common
dnf remplace yum ; sur les systèmes récents, yum est généralement un lien symbolique
install / remove / update diffèrent d'apt — ne pas les mélanger
install -y évite les confirmations, même idée qu'apt
provides retrouve un paquet par fichier ou commande
history liste les transactions et history undo last annule la précédente — salvateur après une mauvaise montée de version
--enablerepo active un dépôt pour une exécution ; --nogpgcheck seulement sans autre solution
Les modules (dnf module list) comptent quand plusieurs versions d'un paquet coexistent
dnf install -y nginx dnf provides /usr/sbin/ss dnf history undo last
RHEL : rpm -qf /usr/bin/ss — -q passe en mode requête et -f cherche par fichier
Debian : dpkg -S /usr/bin/ss ; en cas d'échec, apt-file update puis apt-file search
Si seul le nom de commande est connu : dnf provides '*/ss' ou apt-file search bin/ss
dpkg -L PAQUET liste tous les fichiers installés — utile avant de toucher aux configurations
rpm -qc PAQUET ne liste que les fichiers de configuration (-qc, c'est -q plus -c pour config)
Quand une commande manque, vérifier que le paquet existe avant d'accuser le PATH
rpm -qf /usr/bin/ss dpkg -S /usr/bin/ss dpkg -L nginx-common | head -20
apt clean vide tout le cache .deb ; apt autoclean seulement les entrées périmées
dnf clean all efface le cache et les métadonnées
apt autoremove supprime les paquets installés comme dépendances et devenus inutiles ; --purge leurs configurations aussi
-y reste nécessaire pour autoremove, sinon il s'arrête sur la confirmation
Lancer du -sh d'abord pour confirmer que l'espace est réel (-s total, -h lisible)
Oublier de nettoyer après apt install dans un Dockerfile gaspille des dizaines de Mo par couche
du -sh /var/cache/apt /var/cache/dnf 2>/dev/null apt clean && apt autoremove --purge -y dnf clean all
Neuf problèmes de conteneur sur dix viennent d'un écart entre la configuration supposée et celle réellement active — inspect d'abord, journaux ensuite
-a inclut les conteneurs arrêtés — ne lister que les actifs masque celui qui vient de planter
-q n'imprime que les ID, pratique pour un tube vers docker rm ou docker logs
-s affiche la taille de la couche inscriptible, pour traquer un conteneur qui remplit le disque
--format colonnes personnalisées ; '{{.Names}} {{.Status}}' se lit mieux que le tableau par défaut et convient aux scripts
--filter restreint par condition : status=exited pour les morts, name=web par nom
--no-trunc ne tronque ni les ID longs ni les commandes complètes
-n 5 affiche les 5 créés le plus récemment (raccourci de --last)
Up 3 seconds (restarting) dans Status signale une boucle de redémarrage — voir docker logs --tail
# tous les conteneurs (y compris arrêtés)
docker ps -a --format '{{.Names}} {{.Status}}'
# trouver ceux qui sont arrêtés ou redémarrent en boucle
docker ps -a --filter status=exited -n 5
# qui sature le disque
docker ps -s --format '{{.Names}} {{.Size}}'-f suit le flux, comme tail -f
--tail 200 seulement les 200 dernières lignes — un conteneur ancien en a des dizaines de milliers, sans quoi l'utile défile hors écran
-t préfixe chaque ligne d'un horodatage, indispensable pour recouper avec les journaux de l'hôte
--since 10m seulement les 10 dernières minutes ; une heure absolue comme 2026-10-29T10:00:00 fonctionne aussi
--until se combine avec --since pour découper une fenêtre
-i sur grep signifie insensible à la casse, donc error / ERROR / Error sont tous pris
Les journaux vivent dans /var/lib/docker/containers/<ID>/<ID>-json.log et disparaissent avec le conteneur —
expédier les services importants vers journald ou un collecteur, pas seulement dans docker
docker logs -f --tail 200 -t myapp docker logs --since 10m myapp | grep -i error
-it terminal interactif ; sans -t pas d'invite, sans -i impossible de saisir — passer les deux
-u root exécute en root, pour les images dont l'utilisateur par défaut ne lit pas la configuration
-w /app fixe le répertoire de travail, plus besoin de cd après l'entrée
-e KEY=val injecte une variable temporaire, pour vérifier qu'elle n'a pas été mal transmise
-- empêche docker d'analyser la suite ; requis quand la commande commence par -
-c sur bash exécute le reste comme une chaîne, donc bash -c 'a && b' fait plusieurs étapes d'un coup
-l sur ls donne le format long avec droits et propriétaire
Si exec échoue, vérifier d'abord que le conteneur tourne : docker ps -a, un conteneur Exited fait toujours échouer exec,
le vrai problème étant que le processus principal est déjà mort
Pas de bash dans l'image ? Essayer sh — les images basées sur alpine n'ont souvent que sh
Les images distroless n'ont pas de shell du tout ; utiliser docker cp pour extraire des fichiers, ou un conteneur de débogage temporaire
docker exec -it -u root myapp sh docker exec myapp env | grep -i database docker exec -it myapp bash -c 'ls -l /app'
--format évalue un gabarit Go ; -f en est le raccourci
Chemins utiles : .State.Status, .State.ExitCode,
.NetworkSettings.Networks pour l'IP du conteneur, .Mounts pour les montages, .Config.Env pour les variables,
.HostConfig.RestartPolicy pour la politique de redémarrage
{{json .}} émet du JSON brut, plus simple à passer dans jq
--type image inspecte une image au lieu d'un conteneur
Les valeurs de compose peuvent être écrasées par -e en ligne de commande ; seul inspect montre ce qui s'applique vraiment
.Config.Env contient souvent mots de passe et jetons — filtrer avant de coller ailleurs
docker inspect -f '{{.State.Status}} exit={{.State.ExitCode}}' myapp
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp
docker inspect --format '{{json .Mounts}}' myapp | jq--no-stream prend un instantané unique puis sort, adapté aux scripts et relevés périodiques
--format colonnes personnalisées, faciles à filtrer ou charger en base
-a inclut les conteneurs arrêtés (à 0, mais présents dans la liste)
MEM LIMIT est le plafond du conteneur ; MEM % proche de 100 annonce un OOM imminent
CPU % est rapporté au nombre de cœurs, donc dépasser 100 % pour un conteneur sur une machine multicœur est normal
Quand un conteneur a été OOMKilled, stats ne montre rien (le processus a disparu) ; voir ExitCode 137 via docker inspect
et les lignes oom-kill dans dmesg
docker stats --no-stream --format '{{.Name}} {{.CPUPerc}} {{.MemUsage}}'
docker stats --no-stream -a-t étiquette l'image ; préférer nom:version à latest seul
-f pointe vers un Dockerfile hors du répertoire de build
--no-cache ignore totalement le cache — premier réflexe quand « le code a changé mais pas l'image »
--build-arg passe un argument de build ; ne fonctionne que si le Dockerfile le déclare avec ARG
--target s'arrête à une étape précise, utile pour déboguer les couches intermédiaires d'un build multi-étapes
--progress=plain imprime le journal complet ; le mode TTY par défaut replie la sortie parallèle sur une ligne et masque les erreurs
Le point final est le contexte de build, pas le chemin du Dockerfile — un contexte volumineux ralentit le build, donc exclure node_modules et .git via .dockerignore
docker build -t myapp:1.2.3 . docker build -f docker/Dockerfile.prod --build-arg NODE_ENV=prod --progress=plain -t myapp:prod .
-f choisit un fichier compose ; deux -f superposent des environnements, le dernier l'emportant
-p fixe le nom de projet — par défaut le nom du répertoire, à imposer explicitement quand plusieurs piles cohabitent, sinon conteneurs et réseaux se chevauchent
-d détache l'exécution ; sans lui, compose s'attache et Ctrl+C arrête la pile
--build reconstruit les images avant démarrage
up crée et démarre ; down arrête et supprime conteneurs et réseaux
logs -f --tail 100 suit les journaux ; ajouter un nom de service pour n'en suivre qu'un
config valide et imprime la configuration fusionnée — à lancer après édition, plus sûr que d'enchaîner sur up
compose v2 est docker compose (espace), v1 docker-compose (trait d'union) ; leurs options diffèrent, vérifier laquelle le script utilise
docker compose -f docker-compose.yml -f docker-compose.prod.yml -p shop up -d --build docker compose -p shop logs -f --tail 100 api docker compose -p shop config
system df résume l'usage par images / conteneurs / volumes / cache de build ; -v détaille par objet
prune supprime les objets inutilisés ; par défaut seulement les images pendantes (<none>), -a pour toutes celles qu'aucun conteneur ne référence
--volumes efface aussi les volumes — une base de données peut y vivre, et c'est irréversible
--filter until=24h ne supprime que ce qui date de plus de 24 heures, pour ne rien toucher d'actif
Ordre conseillé : system df -v d'abord pour voir qui occupe, puis un prune ciblé ; ne pas ouvrir avec system prune -a
Les journaux de conteneurs remplissent aussi le disque : /var/lib/docker/containers/*/*-json.log
Le vrai correctif est log-opts avec max-size et max-file dans daemon.json
docker system df -v docker image prune -a --filter until=24h docker system prune --volumes
Le diagnostic Kubernetes suit un ordre quasi fixe : état, événements, journaux, puis shell. Sauter une étape revient à deviner
-A tous les espaces de noms — à ajouter en premier quand une ressource « n'existe pas » ; souvent le mauvais espace de noms
-n choisit un espace de noms ; omis, celui du contexte s'applique
-o wide ajoute les colonnes IP et nœud, essentiel pour les problèmes réseau et de placement
-o yaml imprime la définition complète ; exporter avant toute modification
--show-labels affiche les étiquettes — quand un Service ne trouve pas ses Pods, c'est presque toujours une étiquette discordante
-l app=api filtre par étiquette, plus fiable qu'un grep sur les noms
--sort-by=.status.startTime ordonne par heure de démarrage, le plus récent en dernier
Des noms courts existent : pod/po, svc, deploy, rs, no, cm, secret
kubectl get pod -A -o wide kubectl get pod -n shop -l app=api --show-labels kubectl get pod -A --sort-by=.status.startTime
Le bloc Events en bas est l'endroit le plus informatif du diagnostic Kubernetes —
les raisons d'un ImagePullBackOff, FailedScheduling, Unhealthy ou OOMKilled y figurent
Omettre -n et tomber sur le mauvais espace de noms donne un NotFound nu, sans indice utile
Les événements sont conservés environ une heure par défaut : les relever ; ne pas compter les retrouver plus tard
describe node finit par Allocated resources, montrant les pourcentages de request / limit —
quand un Pod reste Pending, vérifier si les requests sont épuisées (et non si le CPU est vraiment occupé)
describe ne couvre que configuration et événements, jamais les journaux applicatifs — c'est le rôle de kubectl logs
kubectl describe pod -n shop api-7d9f-abc12 kubectl describe node k8s-node-2 | tail -30
-f suit le flux
-n choisit un espace de noms ; omis, celui du contexte s'applique
--tail 200 les 200 dernières lignes ; un Pod ancien inonde le terminal sans cela
-c choisit un conteneur — obligatoire quand un sidecar partage le Pod, sinon une erreur ambiguous liste les conteneurs
--previous lit le conteneur précédent : CrashLoopBackOff redémarre en boucle,
donc le journal courant ne montre que le fragment « mort juste après le démarrage » et la vraie cause est dans le précédent
--since 10m seulement les 10 dernières minutes
--timestamps préfixe chaque ligne d'un horodatage
Les ressources acceptent la forme type/nom comme deploy/api pour lire directement les journaux des Pods d'un Deployment
kubectl logs -f --tail 200 -n shop deploy/api kubectl logs -n shop api-7d9f-abc12 --previous kubectl logs -n shop api-7d9f-abc12 -c istio-proxy --since 10m
-it terminal interactif ; les deux options ensemble
-n choisit l'espace de noms
-c choisit le conteneur, obligatoire pour les Pods multi-conteneurs
-- empêche kubectl d'analyser la suite ; requis quand la commande commence par -
cp prend source et destination, côté Pod écrit sous la forme pod:chemin
cp s'appuie sur tar dans le conteneur, donc les images distroless échouent d'emblée
kubectl debug injecte un conteneur de débogage temporaire qui partage l'espace de noms réseau ;
--image choisit l'image de débogage (busybox suffit) et --target nomme le conteneur à déboguer —
c'est la bonne voie quand l'image de production n'a pas de shell, bien plus rapide que la reconstruire
kubectl exec -it -n shop api-7d9f-abc12 -- sh kubectl cp -n shop api-7d9f-abc12:/app/logs/app.log ./app.log kubectl debug -it -n shop api-7d9f-abc12 --image=busybox --target=api
Même source que le bloc Events de describe, mais lisible sur toutes les ressources d'un coup — idéal quand l'objet fautif est inconnu
-n choisit l'espace de noms
--sort-by=.lastTimestamp ordonne par heure, le plus récent en dernier
--field-selector type=Warning n'affiche que les avertissements, un ordre de grandeur de bruit en moins
--field-selector involvedObject.name=api-7d9f-abc12 cible un objet précis
-A tous les espaces de noms ; les problèmes de cluster (nœud NotReady, évictions) n'apparaissent qu'avec lui
FailedScheduling porte les mots du planificateur, par exemple
insufficient cpu ou node(s) had taint — il dit directement pourquoi le Pod ne peut pas être placé,
sans avoir à deviner entre ressources saturées et taint
Les événements ont un TTL d'environ une heure : les copier sans attendre pendant une boucle de redémarrage
kubectl get events -A --sort-by=.lastTimestamp | tail -20 kubectl get events -n shop --field-selector type=Warning kubectl get events -n shop --field-selector involvedObject.name=api-7d9f-abc12
-f pointe vers un fichier ou un répertoire de manifestes ; -f - lit l'entrée standard
--dry-run=client ne soumet rien, il imprime seulement le YAML final — pour générer un gabarit au lieu de l'écrire
--dry-run=server valide côté serveur (webhooks d'admission compris), bien plus strict que client
-o yaml est ce qui rend la sortie dry-run exploitable
rollout status attend la fin du déploiement progressif et indique précisément où il bloque
rollout undo revient à la révision précédente ; --to-revision=3 en choisit une, listées avec history
rollout restart redémarre les Pods sans changer la configuration — le réflexe après édition d'un ConfigMap ou Secret
-n choisit l'espace de noms
apply est déclaratif et idempotent ; create est impératif et échoue en AlreadyExists à la deuxième exécution
kubectl apply -f deploy/api.yaml --dry-run=client -o yaml > api.gen.yaml kubectl rollout status deploy/api -n shop kubectl rollout undo deploy/api -n shop --to-revision=3 kubectl rollout restart deploy/api -n shop
Format ressource/nom portlocal:portdistant ; pas besoin de NodePort ni d'Ingress
-n choisit l'espace de noms
--address 0.0.0.0 laisse les autres machines du sous-réseau se connecter ; par défaut, écoute sur 127.0.0.1 seulement
Il tunnelise via kube-apiserver et contourne le Service —
donc « l'application est-elle saine » et « un NetworkPolicy bloque-t-il » sont deux questions distinctes, et ceci tranche la première
Fermer le terminal l'arrête ; à garder dans tmux ou screen pour les sessions longues
Ordre de diagnostic : port-forward fonctionne, l'application va bien et le problème est dans Ingress / Service / NetworkPolicy ;
port-forward échoue, le Pod ou le port lui-même est en cause
kubectl port-forward -n shop svc/api 8080:80 kubectl port-forward -n shop --address 0.0.0.0 svc/api 8080:80 kubectl port-forward -n shop pod/api-7d9f-abc12 5432:5432
top node et top pod exigent metrics-server ; sans lui, Metrics API not available
--sort-by=cpu ou --sort-by=memory trie la liste pour faire remonter les gros consommateurs
--containers décompose un Pod par conteneur, pour voir quel sidecar mange la mémoire
-A tous les espaces de noms
Allocated resources dans describe node est le ratio de request / limit —
à vérifier en premier quand le placement échoue : des requests surdimensionnées réservent le nœud alors que le CPU reste au repos
cordon marque un nœud non planifiable sans toucher aux Pods déjà en cours
drain évince les Pods pour maintenance ; --ignore-daemonsets saute les DaemonSet
(qui ne devraient jamais être évincés) et --delete-emptydir-data autorise la suppression des Pods en emptyDir (leurs données temporaires sont perdues)
uncordon rétablit le placement ensuite
kubectl top node kubectl top pod -A --sort-by=memory --containers kubectl drain k8s-node-2 --ignore-daemonsets --delete-emptydir-data
top dit quel processus consomme du CPU, pas ce qu'il attend. Ces outils répondent à la seconde question
-p s'attache à un processus en cours ; on peut aussi préfixer une commande pour tracer dès le démarrage
-f suit les enfants forked — sans lui, on ne voit que le parent en attente pendant que le vrai travail se fait ailleurs
-T imprime la durée de chaque appel (<0.000012>) ; c'est ainsi qu'on trouve l'étape lente
-tt horodatages à la microseconde
-s 200 affiche jusqu'à 200 caractères par chaîne ; la valeur par défaut de 32 tronque les chemins clés en ...
-o out.txt écrit dans un fichier ; des dizaines de milliers de lignes sont illisibles sur un terminal
-c résume le nombre d'appels et le temps total par appel système à la sortie — à lancer d'abord pour trouver la classe coûteuse,
puis -T pour isoler un appel lent
S'attacher avec -p sans aucune sortie signifie un blocage en IO ininterruptible ou sur un verrou noyau ;
regarder ensuite /proc/PID/stack et /proc/PID/wchan
Prudence en production : -f sur tous les appels peut ralentir un processus plusieurs fois, préférer -c ou des filtres
strace -p 1234 -T -tt -s 200 strace -f -p 1234 -o /tmp/st.txt strace -c -p 1234
-e trace= est l'option strace la plus utile, sans conteste
-e trace=file seulement les appels de fichiers (openat, stat, unlink) — pour « fichier introuvable » ou droits insuffisants
-e trace=network seulement les appels réseau (connect, sendto, recvfrom) — pour « connexion impossible » ou délais dépassés
-e trace=openat,connect séparés par des virgules, pour quelques appels précis
-e trace=desc seulement les opérations sur descripteurs, pour voir quel fd bloque
-e signal=SIGSEGV ne suit qu'un signal
Le filtrage ramène des dizaines de milliers de lignes à quelques dizaines — le bon ordre : -c pour trouver la classe coûteuse,
puis -e trace= pour ne suivre que celle-là
openat renvoie ENOENT si le chemin manque, EACCES si les droits manquent, EAGAIN si le non-bloquant n'a pas de données ;
trois corrections totalement différentes, donc ne pas traiter toute erreur comme un problème de droits
Le reste est identique à l'entrée strace -p ci-dessus : -f suit les enfants, -p cible un processus, -T donne le temps par appel, -tt des horodatages microseconde, -o écrit dans un fichier
strace -f -e trace=file -p 1234 -T strace -f -e trace=network -p 1234 -tt strace -o net.txt -e trace=openat,connect -f ./app
perf top est un profil de fonctions chaudes en direct ; sans PID, il couvre la machine entière
-p se limite à un processus
-F 99 échantillonne à 99 Hz — éviter 100 Hz rond pour ne pas créer d'alias avec les ticks et inventer de faux points chauds
-g affiche un graphe d'appels ; la vue est limitée ici, les vraies chaînes demandent record
unknown comme nom de fonction signifie debuginfo manquant ou symboles strippés —
perf n'est pas en cause, ce binaire a été livré sans table de symboles
Nécessite root ou un kernel.perf_event_paranoid relâché ; dans un conteneur, souvent --privileged ou SYS_ADMIN
Différence avec top : top répond « quel processus », perf top « quelle fonction » — c'est de là que part l'optimisation
perf top perf top -p 1234 -F 99 perf top -g
record capture des échantillons, report les parcourt en interactif, script les exporte en texte — le trio
-F 99 échantillonne 99 fois par seconde
-g enregistre les chaînes d'appels ; requis pour les flame graphs, sans lui on n'a que des noms de fonctions isolés
-p cible un processus ; -a échantillonne tous les CPU (système entier)
--call-graph dwarf déroule avec les informations DWARF, plus précis que fp par défaut,
au prix de données un ordre de grandeur plus volumineuses ; si le binaire a été compilé avec -fomit-frame-pointer, il est obligatoire
-o fichier de sortie, perf.data dans le répertoire courant par défaut
-i indique à report ou script quel fichier de données lire
-- préfixe une commande pour que perf la lance et l'échantillonne dès le démarrage
Les flame graphs veulent la sortie repliée de script, passée à stackcollapse de FlameGraph
Dans un conteneur, perf_event_paranoid vaut souvent 2, ce qui cache les symboles noyau ;
sysctl kernel.perf_event_paranoid=-1 ouvre l'accès
perf record -F 99 -g -p 1234 -o /tmp/perf.data perf report -i /tmp/perf.data perf script -i /tmp/perf.data > /tmp/perf.txt
pidstat 1 imprime chaque seconde en continu ; un second nombre borne les itérations
-p ALL montre tous les processus ; -p 1234 un seul
-d IO disque (kB_rd/s lectures, kB_wr/s écritures, iodelay en millisecondes)
-r mémoire (défauts de page, RSS)
-u taux d'occupation CPU (le mode par défaut)
-t décomposition par thread, pour trouver quel thread consomme le CPU — indispensable en multithread
-w changements de contexte : cswch/s volontaires (attente de ressource), nvcswch/s involontaires (préemption)
Fait partie du paquet sysstat, à installer d'abord sur une installation minimale
Meilleur que top pour échantillonner longtemps à intervalle fixe dans un fichier, afin de moyenner ou tracer ensuite
pidstat -p 1234 1 10 pidstat -d -p ALL 1 5 pidstat -t -p 1234 1
iostat -x 1 statistiques étendues, rafraîchies chaque seconde
-x imprime les métriques étendues ; sans lui, on obtient un tableau différent, bien plus pauvre
%util proche de 100 % signifie disque saturé ; await est l'attente moyenne par IO en millisecondes
rkB/s et wkB/s sont le débit — await élevé avec débit faible trahit un disque lent,
await élevé avec débit élevé traduit une charge réelle ; les deux appellent des travaux de capacité différents
vmstat 1 une ligne par seconde : r est la longueur de file d'exécution (durablement au-delà du nombre de cœurs = manque de CPU),
b les processus bloqués, si / so les échanges (non nuls = pression mémoire)
wa est la part du temps CPU à attendre l'IO ; une valeur haute pointe le disque, pas le CPU
-d ici désigne les statistiques disque, n'affichant que ces colonnes
La première ligne est la moyenne depuis le démarrage ; le temps réel commence à la deuxième — ne jamais conclure sur la première
iostat -x 1 5 vmstat 1 5 vmstat -d 1
Quand un processus se bloque et que strace n'imprime rien (état ininterruptible), il faut la pile en espace utilisateur
gdb -p met le processus en pause pendant l'attachement — détacher avant de quitter, sinon il reste gelé, soit une panne de service
-ex exécute une commande gdb puis continue ; en enchaîner plusieurs pour attacher, vider les piles et détacher d'un coup
thread apply all bt vide les piles complètes de tous les threads ; bt suffit en mono-thread
gcore écrit un fichier core pour une analyse hors ligne, plus sûr que le débogage à chaud
-o fixe le chemin du core (sans suffixe .PID ajouté)
Ne pas utiliser gdb sur une JVM — il ne sait pas lire les piles Java, utiliser jstack
Le --call-graph de perf échantillonne dans le temps ; un blocage demande une pile complète, et l'un ne remplace pas l'autre
gdb -p 1234 -ex 'thread apply all bt' -ex detach gcore -o /tmp/core.1234 1234
Les serveurs n'ont souvent que vi, et éditer une configuration passe par lui. Vingt touches suffisent, inutile de tout apprendre
vim +120 fichier saute directement à la ligne 120 — le moyen le plus rapide de suivre un numéro de ligne d'un journal
-u NONE ne charge ni configuration ni plugin, pour écarter un vimrc cassé
-w /tmp/keys.log enregistre toutes les frappes, pour savoir ce qui a été tapé
:w enregistre ; :w! force l'enregistrement (fichier en lecture seule ou droits insuffisants)
:q quitte, refusé s'il y a des modifications non enregistrées ; :q! quitte en les abandonnant
:wq ou :x enregistre et quitte ; :x n'écrit pas quand rien n'a changé
:e! abandonne les modifications et recharge — quand le fichier a été saccagé et qu'il faut repartir
:saveas /tmp/a.conf enregistre sous, l'étape de sauvegarde standard avant d'éditer une configuration
Fichier en lecture seule édité par erreur ? :q! est le rattrapage le plus rapide — ne pas chercher un bouton d'enregistrement
vim +120 /etc/nginx/nginx.conf vim -u NONE /etc/nginx/nginx.conf :w 保存 :q! quitter sans enregistrer :wq 保存退出 :x n'enregistrer que s'il y a des modifications :e! 重新加载 :saveas /tmp/a.conf enregistrer sous
gg première ligne ; G dernière ; 42G va à la ligne 42
0 début de ligne ; ^ premier caractère non blanc (à utiliser pour aligner l'indentation) ; $ fin de ligne
w début du mot suivant ; b début du mot précédent ; e fin du mot courant
Ctrl+f page suivante ; Ctrl+b page précédente ; Ctrl+d demi-page vers le bas ; Ctrl+u demi-page vers le haut
% saute à la parenthèse correspondante — le moyen rapide de trouver la fin d'un bloc de configuration
'' revient à la position précédente (deux apostrophes) ; Entrée fonctionne aussi
Éviter les flèches sur les gros fichiers ; hjkl avec les déplacements ci-dessus va bien plus vite
:set nu avant d'éditer, pour que les numéros de ligne des erreurs aient un sens
gg 文件首行 G dernière ligne du fichier 42G 第 42 行 0 début de ligne ^ 行首首个非空白 $ fin de ligne w / b / e 下词首 / 上词首 / 词尾 Ctrl+f 下一页 Ctrl+b page précédente % 跳到匹配的括号 '' revenir à la position précédente
dd supprime une ligne — en réalité une coupe, donc p la recolle
yy copie une ligne ; 5yy en copie cinq
p colle sous la ligne courante ; P colle au-dessus
u annule ; Ctrl+r rétablit (quand l'annulation est allée trop loin)
x supprime un caractère ; r remplace le caractère sous le curseur sans passer en insertion
cc réécrit toute la ligne, en vidant le contenu mais en gardant l'indentation
d$ supprime jusqu'à la fin de ligne ; d0 jusqu'au début ; dw supprime un mot
. répète la dernière modification — la touche la plus précieuse de Vim ; supprimer 20 lignes, c'est dd puis 19 fois .
i insère avant le curseur ; a après ; o ouvre une ligne en dessous ; O au-dessus
Esc revient en mode normal, et l'oublier est le meilleur moyen de taper une ligne de charabia
dd / yy / p supprimer une ligne / copier une ligne / coller 5yy copier 5 lignes u / Ctrl+r annuler / rétablir x / r supprimer un caractère / remplacer un caractère cc réécrire toute la ligne (indentation conservée) . répéter la dernière modification (la touche la plus rentable) i / a / o / O insérer avant / après / nouvelle ligne en dessous / nouvelle ligne au-dessus
/pattern cherche vers l'avant ; ?pattern vers l'arrière
n correspondance suivante ; N précédente
* cherche le mot sous le curseur sans le taper ; # cherche en arrière
:set hlsearch surligne toutes les correspondances ; :noh efface le surlignage (quand l'écran est tout jaune)
:%s/ancien/nouveau/g remplace partout ; g signifie toutes les occurrences d'une ligne, sans lui la première seulement
:%s/ancien/nouveau/gc demande avant chaque remplacement — toujours ajouter c pour les fichiers de configuration ; un mauvais remplacement met un service à terre
:10,20s/ancien/nouveau/g se limite aux lignes 10 à 20
:.,$s/ancien/nouveau/g de la ligne courante à la fin du fichier
Le séparateur est interchangeable : :s#/usr/local/old#/usr/local/new#g évite d'échapper chaque barre oblique d'un chemin
Compter les correspondances avec /pattern avant de remplacer, pour savoir ce qui va changer
:%s/old/new/g remplacement global (toutes les occurrences de chaque ligne) :%s/old/new/gc confirmer avant chaque remplacement (toujours ajouter c pour modifier une config) :10,20s/old/new/g remplacer seulement les lignes 10 à 20 :.,$s/old/new/g de la ligne courante à la fin du fichier :s#/usr/local/old#/usr/local/new#g changer de séparateur, plus besoin d'échapper les chemins :noh annuler la surbrillance de recherche
Ctrl+v commence la sélection en bloc ; déplacer le curseur pour balayer une colonne — l'outil des commentaires en masse et de l'alignement
Bloc sélectionné, I insère simultanément au début de chaque ligne et ne s'applique qu'à Esc (oublier Esc donne l'impression que rien ne s'est passé)
A ajoute à la fin de chaque ligne sélectionnée
c remplace tout le bloc, d le supprime
:g/pattern/d supprime toutes les lignes correspondantes
:g/pattern/s/old/new/g remplace seulement dans les lignes correspondantes, plus sûr qu'un remplacement global
:sort trie les lignes sélectionnées ; :sort u trie et supprime les doublons
:normal @a exécute la macro du registre a une fois par ligne sélectionnée, pour les tâches répétitives
:set nu d'abord, pour ne pas se tromper de plage pendant une édition en bloc
Ctrl+v I Esc après sélection de bloc, insérer en début de chaque ligne (commentaire groupé) Ctrl+v A Esc après sélection de bloc, ajouter en fin de chaque ligne :g/^#/d supprimer toutes les lignes commençant par # :g/ERROR/s/old/new/g remplacer uniquement dans les lignes contenant ERROR :sort u trier et dédupliquer les lignes sélectionnées
Coller du code depuis un navigateur ou Windows et l'indentation part vers la droite : :set paste avant, :set nopaste après
(ou :set pastetoggle=<F2> pour basculer avec F2)
Un fichier édité sous Windows affiche ^M en fin de ligne : :set ff=unix puis :w
:set ff seul indique si le fichier est actuellement en dos ou unix
Caractères chinois illisibles : :set fileencoding=utf-8 puis enregistrer ; pour seulement lire correctement sans modifier le fichier,
utiliser :e ++enc=gbk pour le relire en GBK
:w !sudo tee % résout « sudo oublié, enregistrement impossible » —
il réécrit le tampon via sudo sous le nom courant, plus rapide que rouvrir et refaire les modifications
Ces réglages sont par session ; les mettre dans ~/.vimrc pour les rendre permanents
:set paste désactiver l'auto-indentation avant de coller :set nopaste réactiver après le collage :set ff=unix supprimer les ^M de Windows :set fileencoding=utf-8 corriger l'encodage puis enregistrer :w !sudo tee % forcer l'écriture quand sudo a été oublié
« Le serveur rame » n'est pas un problème mais quatre : CPU, mémoire, IO, réseau. Une commande dit lequel
uptime affiche les load average sur 1/5/15 minutes — au-delà du nombre de cœurs, il y a de la file
Une charge haute avec un CPU au repos (%id élevé dans top) pointe l'attente IO — regarder la colonne wa
Dans free -h, lire la colonne available, pas free
Vérifier df -h (capacité) et df -i (inodes) ensemble ; un disque plein produit des pannes étranges
vmstat 1 5 échantillonne une fois par seconde, cinq fois — regarder r (file d'exécution), b (bloqués), wa (attente IO), si/so (échanges)
Identifier la couche avant d'agir — bien plus efficace que redémarrer les services
uptime free -h df -h; df -i vmstat 1 5
Regarder la colonne wa de vmstat : durablement au-dessus de 10, l'IO freine
Puis la colonne b (processus bloqués) : au-delà du nombre de cœurs, beaucoup attendent une ressource
Trouver le coupable avec pidstat -d 1 5 (-d ne rapporte que l'IO, chaque seconde, cinq fois)
Dans ps -eo, -e sélectionne tous les processus et -o les colonnes ; la colonne state révèle D (ininterruptible)
awk '$1=="D"' filtre les processus bloqués en état D — ceux qui attendent l'IO
Causes typiques : parcours de table complet, écritures de journaux synchrones, sauvegarde en cours, NFS ou disque cloud instable
Un processus en état D ne se tue pas — attendre l'IO ou redémarrer la machine
vmstat 1 5 pidstat -d 1 5 ps -eo state,pid,cmd | awk '$1=="D"'
Dans free -h, available est ce que de nouveaux processus peuvent encore obtenir ; un free bas est normal — Linux utilise la mémoire libre comme cache
Un buff/cache élevé n'est pas un problème ; il est récupéré à la demande
Un swap très utilisé traduit une vraie pression ; des si/so durablement non nuls dans vmstat indiquent du thrashing
Trier les processus : ps -eo pid,rss,comm --sort=-rss (-e tout, -o colonnes, le moins pour décroissant)
RSS est l'occupation réelle ; VSZ l'espace d'adressage, sans grand intérêt
Dans un conteneur, c'est la limite cgroup qui compte (/sys/fs/cgroup/memory.max), pas le total de l'hôte
Pour une fuite suspectée, comparer deux relevés RSS à dix minutes d'intervalle et voir lequel ne fait que croître
free -h vmstat 1 5 # voir si / so ps -eo pid,rss,comm --sort=-rss | head
Couche 1 : curl -w décompose DNS, connexion, premier octet et total
curl -o /dev/null jette le corps, -s tait la progression, -w imprime un format propre — ensemble, on ne voit que des chiffres
Couche 2 : curl 127.0.0.1 sur le serveur même, pour écarter réseau et passerelle
Couche 3 : comparer les durées du journal applicatif avec $request_time et $upstream_response_time de nginx
Un grand écart indique un proxy intermédiaire ou un pool de connexions
Ne pas oublier le journal des requêtes lentes de la base — beaucoup d'« endpoints lents » sont des requêtes SQL lentes
Un total élevé avec un temps amont faible signifie souvent limitation de débit ou mise en file
curl -o /dev/null -s -w 'dns:%{time_namelookup} conn:%{time_connect} ttfb:%{time_starttransfer} total:%{time_total}\n' https://example.com/api
# ajouter dans nginx log_format :
# $request_time $upstream_response_timeping -c 50 envoie 50 paquets et rapporte loss% et mdev (gigue) ; un seul ping ne prouve rien
ping -M do -s 1472 teste le MTU du chemin : -M do interdit la fragmentation et -s fixe la taille utile
mtr -r -c 50 : -r imprime un rapport au lieu d'un écran animé, -c fixe le nombre de paquets, et il bat traceroute en montrant la perte par saut
Une limitation ICMP peut simuler une perte ; des sondes TCP (nc -z, curl) sont plus fiables
ethtool -S CARTE affiche les compteurs d'erreurs et de pertes rx/tx (-S pour statistics)
grep -iE signifie ignorer la casse plus expressions étendues
Une perte dans la même baie pointe la carte ou le câblage ; entre régions, l'opérateur
ping -c 50 10.0.0.5 mtr -r -c 50 10.0.0.5 ethtool -S eth0 | grep -iE 'err|drop'
Sur une machine inconnue, lancer ceci d'abord pour la vue d'ensemble, puis décider quoi creuser
Chaque ligne est étiquetée par la question à laquelle elle répond — ne pas copier comme une boîte noire
Aide-mémoire des options (toutes vues plus haut) : --sort=-%cpu CPU décroissant, -h lisible, -i inodes
grep -vE signifie inversion plus expressions étendues, ici pour écarter les systèmes de fichiers virtuels comme tmpfs
ss -tulnp se décompose en t/TCP, u/UDP, l/écoute, n/numérique, p/processus
Côté systemctl, --failed ne montre que les unités en échec et --no-pager évite le pagineur
Côté dmesg, -T donne une heure lisible et -l filtre par niveau
Tout ici est en lecture seule, sans risque en production, et prêt à coller dans un ticket
echo "== 负载 =="; uptime echo "== CPU 前 5 =="; ps aux --sort=-%cpu | head -6 echo "== 内存前 5 =="; ps aux --sort=-%mem | head -6 echo "== 内存 =="; free -h echo "== 磁盘 =="; df -h | grep -vE 'tmpfs|overlay' echo "== inode =="; df -i | grep -vE 'tmpfs|overlay' echo "== 监听端口 =="; ss -tulnp echo "== 失败服务 =="; systemctl list-units --failed --no-pager echo "== 内核错误 =="; dmesg -T -l err | tail -10