İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı
Yeniden çalıştırılmaları güvenli işlemler tasarlayın ve güvenilmez dış çağrılar için üstel geri çekilme ekleyin.
İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı, CoddyKit'te ücretsiz bir DevOps Bootcamp dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, DevOps Bootcamp öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. DevOps Bootcamp kursu toplamda 4 dersten oluşur.
İdempotensi Nedir ve Neden Önemlidir
İdempotensi, aynı işlemin birden çok kez çalıştırılmasının, bir kez çalıştırılmasıyla aynı sonucu üretmesi demektir. Bash betiklerinde bu kritik öneme sahiptir; çünkü betikler çökebilir, ağ bağlantıları kesilebilir ve insanlar işlemleri yanlışlıkla yeniden çalıştırabilir.
- Bir kullanıcıyı iki kez oluşturan idempotent olmayan bir betik başarısız olabilir veya verileri çoğaltabilir.
- İdempotent bir betik önce şunu denetler: Bu zaten var mı?
- İdempotent betikler cron işlerinde, CI işlem hatlarında ve yeniden deneme döngülerinde güvenle kullanılabilir.
Temel kural şudur: eyleme geçmeden önce denetleyin. Her yıkıcı veya oluşturucu işlem, bir ön koşul sınamasıyla korunmalıdır.
Dosya ve Dizin Oluşturmayı Koruma
En yaygın idempotensi kalıbı, oluşturmadan önce bir kaynağın zaten var olup olmadığını denetlemektir. Bash bunun için kısa tek satırlık komutlar sunar.
[ -d dir ]— dizin varsa doğru[ -f file ]— normal dosya varsa doğrumkdir -p— yalnızca yoksa dizin oluşturur (yerleşik idempotensi)
Kullanılabildiğinde elle yapılan denetimler yerine -p ve --no-clobber gibi yerleşik seçenekleri tercih edin; bunlar atomiktir ve yarış koşullarına karşı güvenlidir.
#!/usr/bin/env bash
set -euo pipefail
CONFIG_DIR="$HOME/.myapp"
CONFIG_FILE="$CONFIG_DIR/config.ini"
# Idempotent: mkdir -p never fails if dir already exists
mkdir -p "$CONFIG_DIR"
# Idempotent: only write config if it doesn't exist yet
if [ ! -f "$CONFIG_FILE" ]; then
echo '[defaults]' > "$CONFIG_FILE"
echo 'timeout=30' >> "$CONFIG_FILE"
echo "Created $CONFIG_FILE"
else
echo "Config already exists, skipping."
fiİdempotent Kullanıcı ve Grup Yönetimi
Kullanıcı veya grup ekleme gibi sistem yönetimi görevleri idempotent olmalıdır; aynı makinede betiğin yeniden çalıştırılması hata vermemeli veya yinelenen kayıtlar oluşturmamalıdır.
id username, kullanıcı varsa 0 döndürürgetent group groupname, bir grubu denetler- Betiğin yeniden yürütülmesinin güvenli olması için her işlemi bir koruma içine alın
Bu kalıp, Ansible gibi yapılandırma yönetimi araçlarının temelini oluşturur; her görev, korumalı ve idempotent bir işlemdir.
#!/usr/bin/env bash
set -euo pipefail
APP_USER="apprunner"
APP_GROUP="appgroup"
# Idempotent group creation
if ! getent group "$APP_GROUP" &>/dev/null; then
groupadd "$APP_GROUP"
echo "Group '$APP_GROUP' created."
else
echo "Group '$APP_GROUP' already exists."
fi
# Idempotent user creation
if ! id "$APP_USER" &>/dev/null; then
useradd -m -g "$APP_GROUP" -s /bin/bash "$APP_USER"
echo "User '$APP_USER' created."
else
echo "User '$APP_USER' already exists."
fiEşzamanlı Çalışmaları Önlemek için Kilit Dosyalarını Kullanma
İdempotent bir betik bile iki örnek eşzamanlı çalışırsa sorunlara yol açabilir. Bir kilit dosyası, aynı anda yalnızca bir örneğin çalışmasını sağlar.
- Başlangıçta bir kilit dosyası oluşturun; çıkışta kaldırın.
- Betiğin çalışması kesintiye uğrasa bile kilidin temizlenmesi için bir
trapkullanın. - Tek bir yol üzerinde
mkdirkullanmak çoğu Linux dosya sisteminde atomiktir; kilitleme içintouch'tan daha güvenlidir.
Kilit olmadan yavaş bir cron işi ile elle yapılan yeniden çalıştırma çakışabilir ve paylaşılan durumu bozabilir.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/myapp_deploy.lock"
# Atomic lock acquisition using mkdir
if ! mkdir "$LOCKFILE" 2>/dev/null; then
echo "ERROR: Another instance is running (lock: $LOCKFILE). Exiting." >&2
exit 1
fi
# Guarantee lock removal on any exit
trap 'rmdir "$LOCKFILE"; echo "Lock released."' EXIT
echo "Lock acquired. Running deployment..."
sleep 2 # simulate work
echo "Deployment complete."Tamamlanan Adımları Durum Dosyasıyla İzleme
Çok adımlı betikler (geçişler, kurulumlar, hazırlama işlemleri) için hangi adımların tamamlandığını bir durum dosyası kullanarak izleyebilirsiniz. Her adım yürütülmeden önce durum dosyasını denetler ve tamamlandığında dosyaya yazar.
- Ucuz ve taşınabilirdir; veritabanı gerektirmez.
- Başarısız olan bir betiğin kaldığı yerden devam etmesini sağlar.
- Durumu
/var/lib/myapp/veya~/.myapp/state/gibi tahmin edilebilir bir konumda saklayın.
Bu kalıp, apt, cloud-init ve veritabanı geçişi çerçeveleri gibi başlıca araçlar tarafından kullanılır.
#!/usr/bin/env bash
set -euo pipefail
STATE_DIR="/tmp/myapp_state"
mkdir -p "$STATE_DIR"
run_step() {
local step_name="$1"
local step_cmd="$2"
local marker="$STATE_DIR/${step_name}.done"
if [ -f "$marker" ]; then
echo "[SKIP] $step_name already completed."
return 0
fi
echo "[RUN] $step_name ..."
eval "$step_cmd"
touch "$marker"
echo "[DONE] $step_name"
}
run_step "install_deps" "echo 'Installing dependencies...'"
run_step "configure_db" "echo 'Configuring database...'"
run_step "start_service" "echo 'Starting service...'"Yeniden Deneme Mantığına Giriş
Harici çağrılar — HTTP istekleri, DNS aramaları ve bulut API çağrıları — doğaları gereği güvenilmezdir. Tek bir başarısızlık bütün betiğinizi durdurmamalıdır. Yeniden deneme mantığı, başarısız bir komutu otomatik olarak yeniden dener.
- Basit yeniden deneme: Komut başarılı olana kadar N kez döngüye girin.
- Sonsuz döngüleri önlemek için her zaman bir en fazla yeniden deneme sayısı belirleyin.
- Başarısızlıkların tanılanabilmesi için her denemeyi günlüğe kaydedin.
En basit yeniden deneme işlevi, herhangi bir komutu sarar ve sabit sayıda deneme boyunca sabit bir gecikmeyle yeniden dener. Bu, birçok kullanım için yeterlidir; ancak yük altında kritik bir kusuru vardır ve bu kusur bir sonraki bölümde ele alınacaktır.
#!/usr/bin/env bash
set -euo pipefail
# Simple fixed-delay retry (3 attempts, 2s apart)
retry() {
local max_attempts=3
local delay=2
local attempt=1
until "$@"; do
if (( attempt >= max_attempts )); then
echo "ERROR: Command failed after $max_attempts attempts: $*" >&2
return 1
fi
echo "Attempt $attempt failed. Retrying in ${delay}s..." >&2
sleep "$delay"
(( attempt++ ))
done
}
# Example: retry a curl call
retry curl --silent --fail --max-time 5 https://httpbin.org/get -o /dev/null
echo "Request succeeded."Damgalı Sürü Problemi
Bir başarısızlıktan sonra birçok istemci aynı anda yeniden denediğinde bir damgalı sürü oluşturur; tüm istemciler aynı sabit aralıklarla yeniden deneyerek sunucuya tam olarak aynı anda yüklenir ve kurtarmayı olanaksız hâle getirir.
- 100 betik her 5 saniyede bir yeniden deniyor → her 5 saniyede 100 eşzamanlı istek.
- Sunucu zaten zorlanıyordur; eşzamanlı yük durumu daha da kötüleştirir.
- Çözüm üstel geri çekilmedir: her başarısızlıktan sonra bekleme süresini iki katına çıkarın.
- İstemciler arasındaki yeniden denemeleri eşzamanlılıktan çıkarmak için jitter (rastgele sapma) ekleyin.
Jitter içeren üstel geri çekilme, AWS SDK'leri, Google Cloud istemcileri ve tüm büyük dağıtık sistemler tarafından kullanılan sektör standardıdır.
Üstel Geri Çekilmeyi Uygulama
Üstel geri çekilme, her hatadan sonra bekleme süresini üstel olarak artırır: 1 sn, 2 sn, 4 sn, 8 sn, 16 sn... Bu, toplam yükü azaltırken uzak sistemin toparlanması için zaman tanır.
Formül: delay = base * (2 ^ attempt)
- Bekleme sürelerinin sınırsızca büyümesini önlemek için bir üst sınır (en fazla gecikme) belirleyin.
- Ayarlanacak parametreler:
base_delay,max_delay,max_attempts.
Bu işlev yeniden kullanılabilir; herhangi bir komutu bağımsız değişken olarak kendisine aktarabilirsiniz.
#!/usr/bin/env bash
set -euo pipefail
retry_with_backoff() {
local max_attempts="${RETRY_MAX_ATTEMPTS:-5}"
local base_delay="${RETRY_BASE_DELAY:-1}"
local max_delay="${RETRY_MAX_DELAY:-30}"
local attempt=0
local delay="$base_delay"
until "$@"; do
(( attempt++ ))
if (( attempt >= max_attempts )); then
echo "ERROR: '$*' failed after $max_attempts attempts." >&2
return 1
fi
echo "Attempt $attempt failed. Backing off for ${delay}s..." >&2
sleep "$delay"
# Double the delay, but cap it
delay=$(( delay * 2 ))
(( delay > max_delay )) && delay=$max_delay
done
echo "Command succeeded on attempt $(( attempt + 1 ))."
}
retry_with_backoff echo "Simulated success"Geri Çekilmeye Jitter Ekleme
Jitter, geri çekilme gecikmesine rastlantısallık ekler. Tüm istemciler aynı anda başlarsa üstel geri çekilme kullanılsa bile yeniden denemeler eş zamanlı gerçekleşir. Jitter bu eşzamanlılığı bozar.
Yaygın iki jitter stratejisi:
- Tam jitter:
sleep random(0, cap)— en geniş dağılımı ve en düşük tepe yükünü sağlar. - Eşit jitter:
sleep cap/2 + random(0, cap/2)— en az bekleme süresini garanti eder ve hemen art arda istek gönderilmesini önler.
Bash'te rastgele sayılar üretmek için $RANDOM (0–32767) kullanın. Modüler aritmetik ile bu değeri gecikme aralığınıza ölçeklendirin.
#!/usr/bin/env bash
set -euo pipefail
retry_with_jitter() {
local max_attempts="${1:-5}"; shift
local base_delay=1
local max_delay=32
local attempt=0
local cap=$base_delay
until "$@"; do
(( attempt++ ))
if (( attempt >= max_attempts )); then
echo "ERROR: Giving up after $max_attempts attempts." >&2
return 1
fi
# Full jitter: random value in [0, cap]
local jitter=$(( RANDOM % (cap + 1) ))
echo "Attempt $attempt failed. Sleeping ${jitter}s (cap=${cap}s)..." >&2
sleep "$jitter"
# Grow cap exponentially, bounded by max_delay
cap=$(( cap * 2 ))
(( cap > max_delay )) && cap=$max_delay
done
}
retry_with_jitter 4 curl --silent --fail --max-time 3 https://httpbin.org/get -o /dev/null
echo "Done."Gerçek Bir İş Akışında İdempotensi ve Yeniden Denemeyi Birleştirme
Üretim betiklerinde idempotensi ve yeniden deneme mantığı birlikte çalışır. Tipik bir dağıtım iş akışı şunları yapabilir:
- Kilit edinme (eşzamanlı çalışmaları önleme)
- Durum dosyalarını denetleme (tamamlanan adımları atlama)
- Harici çağrılar için geri çekilmeli yeniden deneme kullanma (indirme, API, DNS)
- Adımları yalnızca başarının doğrulanmasından sonra tamamlandı olarak işaretleme
- Kilidi
traparacılığıyla serbest bırakma
Bu birleşim, betiklerin bir çökmeden, zaman aşımından veya elle iptal edilmesinden sonra bile, sistemi bozuk durumda bırakmadan herhangi bir noktada güvenle yeniden çalıştırılabilmesini sağlar.
#!/usr/bin/env bash
set -euo pipefail
STATE_DIR="/tmp/deploy_state" && mkdir -p "$STATE_DIR"
LOCK="/tmp/deploy.lock"
mkdir "$LOCK" 2>/dev/null || { echo "Already running."; exit 1; }
trap 'rmdir "$LOCK"' EXIT
step_done() { [ -f "$STATE_DIR/$1.done" ]; }
mark_done() { touch "$STATE_DIR/$1.done"; }
retry_backoff() {
local attempt=0 delay=1
until "${@:2}"; do
(( ++attempt >= $1 )) && { echo "Failed after $1 attempts."; return 1; }
echo "Retry $attempt in ${delay}s..."; sleep $delay; delay=$(( delay * 2 ))
done
}
if ! step_done "download_artifact"; then
retry_backoff 4 curl -fsSL https://httpbin.org/get -o /tmp/artifact.json
mark_done "download_artifact"
echo "[DONE] download_artifact"
else
echo "[SKIP] download_artifact"
fi
echo "Deployment finished successfully."Yeniden Denenemeyen Hataları Yönetme
Tüm hatalar yeniden denenmemelidir. 404 Not Found veya 403 Forbidden yanıtlarını yeniden denemek gereksizdir; insan müdahalesi olmadan hiçbir zaman başarılı olmazlar. Yeniden deneme mantığınız şunları birbirinden ayırmalıdır:
- Geçici hatalar — ağ zaman aşımı, 503 Service Unavailable, DNS hatası → yeniden dene
- Kalıcı hatalar — 401 Unauthorized, 404 Not Found, geçersiz girdi → hemen başarısız ol
curl ile HTTP durum kodunu denetleyin ve 4xx yanıtlarında yeniden denemeleri atlayın. Durumu gövdeden ayrı yakalamak için --write-out '%{http_code}' kullanın.
#!/usr/bin/env bash
set -euo pipefail
fetch_with_retry() {
local url="$1"
local max_attempts=4
local delay=1
local attempt=0
local http_code
until http_code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$url"); do
: # curl itself failed (network error)
(( ++attempt >= max_attempts )) && { echo "Network error, giving up."; return 1; }
sleep $delay; delay=$(( delay * 2 ))
done
# Permanent client errors — do not retry
if [[ "$http_code" =~ ^4 ]]; then
echo "ERROR: HTTP $http_code for $url — not retrying." >&2
return 1
fi
# Transient server errors — retry
if [[ "$http_code" =~ ^5 ]]; then
(( ++attempt >= max_attempts )) && { echo "Server error, giving up."; return 1; }
echo "HTTP $http_code — backing off ${delay}s..."
sleep $delay; delay=$(( delay * 2 ))
fetch_with_retry "$url"
return
fi
echo "HTTP $http_code — success."
}
fetch_with_retry "https://httpbin.org/status/200"Bilgi Denetimi: Geri Çekilme Stratejisi Seçimi
Bir dağıtım betiği, bir S3 paketinden sürüm yapıtı indiriyor. Yakın zamanda yaşanan bir olayda, kısa süreli bir S3 kesintisi nedeniyle 80 işlem hattı örneğinin tamamı aynı anda başarısız oldu. S3 10 saniye sonra düzeldiğinde, 80 örneğin tamamı aynı anda yeniden denedi; bu da başka bir aşırı yüke yol açarak kesintiyi 3 dakika uzattı.
Gelecekteki olaylarda bu sürü etkili zincirleme yüklenmesini en iyi hangi yeniden deneme stratejisi önler?
Özet: İdempotensi ve Geri Çekilmeli Yeniden Deneme
Bu derste güvenle yeniden çalıştırılabilen ve geçici arızalara dayanıklı Bash betiklerinin nasıl tasarlanacağını öğrendiniz.
İdempotensi kalıpları:
- Her işlemi bir varlık denetimiyle koruyun (
[ -f ],[ -d ],id,getent). - Uygun olduğunda
mkdir -pve diğer yerleşik idempotent seçenekleri kullanın. - Eşzamanlı çalışmaları önlemek için kilit dosyaları (atomik
mkdir) kullanın. - Hata sonrasında devam etmeye olanak tanımak için durum dosyaları (adım başına işaretçi dosyaları) kullanın.
Geri çekilmeli yeniden deneme kalıpları:
- Her zaman bir en fazla deneme sayısı belirleyin; sonsuza kadar yeniden denemeyin.
- Üstel geri çekilme kullanın: her hatadan sonra gecikmeyi iki katına çıkarın.
- Sürü etkili eşzamanlılığı önlemek için jitter (rastlantısallık) ekleyin.
- Geçici (yeniden dene) ve kalıcı (hızlıca başarısız ol) hataları birbirinden ayırın.
Bu kalıpların birleştirilmesi, üretim ortamına uygun; güvenli, gözlemlenebilir ve kendi kendini iyileştirebilen betikler oluşturur.
Sıkça Sorulan Sorular
“İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı” dersi ücretsiz mi?
Evet — “İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve DevOps Bootcamp kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. DevOps Bootcamp kursu toplamda 4 dersten oluşur.
“İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı” dersinde ne öğreneceğim?
Yeniden çalıştırılmaları güvenli işlemler tasarlayın ve güvenilmez dış çağrılar için üstel geri çekilme ekleyin. DevOps Bootcamp ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
DevOps Bootcamp öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te DevOps Bootcamp, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.
“İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu DevOps Bootcamp dersinde kod yazıp çalıştırabilir miyim?
Evet. Her DevOps Bootcamp dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- set -euo pipefail ile Katı Mod
- Temizleme ve Sinyaller için Trap İşleyicileri
- Güvenli Geçici Dosyalar ve Kilit Dizinleri
- İdempotent Betikler ve Geri Çekilmeli Yeniden Deneme Mantığı