Backup, Deployment, dan File Transfer

Backup, Deployment, dan File Transfer

Bitnesia • Aug 30, 2026 85 •EN

Bab 13.2 pernah membahas rsync dan tar untuk backup folder pribadi di desktop, dan sub-bab itu sendiri sudah menjanjikan bahwa kedua tool ini "dibahas lebih dalam lagi untuk konteks server di Bab 21". Sekarang saatnya menepati janji itu. Kebutuhan backup di server jauh lebih berat dibanding di laptop pribadi: Sysadmin harus menyalin data ke lokasi lain (idealnya mesin fisik berbeda, bukan cuma folder lain di disk yang sama), menyusun rotasi berlapis supaya kesalahan yang baru disadari seminggu kemudian tetap bisa dipulihkan, dan memastikan seluruh prosesnya berjalan sendiri tanpa perlu berdiri di depan terminal mengetik password satu per satu. Bab ini membahas tiga kebutuhan operasional yang saling berkaitan erat: backup otomatis dengan strategi snapshot hemat ruang dan rotasi berlapis, transfer file aman lewat SSH tanpa interaksi manual, serta deployment (proses memindahkan kode versi terbaru ke server yang sedang melayani pengguna) sederhana yang menarik kode terbaru dan me-restart service secara aman, lengkap dengan jalur rollback (mekanisme mengembalikan sistem ke kondisi sebelumnya) kalau ada yang gagal. Studi kasus penutup menggabungkan ketiganya jadi satu pipeline, sekaligus memakai ulang fungsi kirim_alert() dari Bab 20.4.2 supaya Sysadmin langsung tahu begitu proses backup atau deploy bermasalah, bukan menemukannya keesokan hari lewat keluhan user.

21.1 Backup Otomatis dan Rotasi Berlapis

Tiga sub-bab pertama ini membangun strategi backup server yang realistis: bukan cuma menyalin file sekali, tapi menyimpan banyak titik pemulihan sekaligus tanpa membengkakkan kebutuhan disk secara linear.

21.1.1 Backup Incremental dengan Konsep Hard Link

Menjalankan rsync -av --delete ke satu folder tujuan yang sama tiap malam, persis pola 13.2.2, memang membuat tujuan selalu identik dengan sumber. Masalahnya, begitu --delete aktif, backup semalam sebelumnya ikut tertimpa; kalau file penting ternyata terhapus tanpa sengaja tiga hari lalu dan baru disadari hari ini, backup satu-satunya itu sudah lebih dulu "ikut menghapus" file yang sama. Solusinya bukan berhenti memakai --delete (itu bikin backup makin lama makin penuh sampah), melainkan menyimpan banyak snapshot harian sekaligus, masing-masing mewakili kondisi lengkap di satu titik waktu tertentu.

Menyimpan snapshot lengkap tiap hari lewat tar jelas boros disk kalau sebagian besar isinya tidak berubah dari hari ke hari. Di sinilah konsep hard link berperan. Berbeda dengan symbolic link yang sekadar berupa "pointer" berisi path menuju file lain, hard link adalah entri direktori tambahan yang menunjuk langsung ke inode (blok data fisik) yang sama persis di disk. Dua nama file yang saling ber-hard-link pada dasarnya adalah satu data yang sama, cuma direferensikan lewat dua nama; menghapus salah satu nama tidak menghapus datanya selama nama yang lain masih ada, dan yang lebih penting untuk backup, membuat hard link tidak memakan tambahan ruang disk sama sekali karena tidak ada data yang benar-benar disalin.

Opsi --link-dest=DIR pada rsync memanfaatkan sifat ini: untuk setiap file yang isinya identik dengan versi di DIR (biasanya snapshot hasil backup sebelumnya), rsync membuat hard link ke file itu alih-alih menyalin ulang datanya, sesuai dokumentasi resmi rsync. Hasilnya, setiap folder snapshot terlihat dan berfungsi sebagai backup lengkap dan bisa dijelajahi mandiri, padahal secara fisik cuma file yang benar-benar berubah yang memakai ruang disk baru. Teknik ini kadang disebut "poor man's Time Machine" karena meniru cara kerja fitur backup incremental bawaan macOS, dan sudah lama jadi pola standar backup server berbasis rsync. Satu syarat pentingnya: karena hard link tidak bisa melintasi batas filesystem, folder tujuan snapshot dan folder --link-dest yang dirujuk wajib berada di filesystem/partisi yang sama.

21.1.2 Implementasi Snapshot Harian

BACKUP_ROOT="/backup/aplikasi"
SNAPSHOT_DIR="$BACKUP_ROOT/$(date +%Y-%m-%d)"
LATEST_LINK="$BACKUP_ROOT/latest"

rsync -a --delete \
    --link-dest="$LATEST_LINK" \
    /var/www/aplikasi/ \
    "$SNAPSHOT_DIR/"

rm -f "$LATEST_LINK"
ln -s "$SNAPSHOT_DIR" "$LATEST_LINK"

Setiap kali script ini berjalan, rsync membandingkan sumber dengan isi $LATEST_LINK, symbolic link yang selalu menunjuk ke snapshot paling baru. File yang tidak berubah cukup di-hard-link, file yang berubah atau baru benar-benar disalin penuh ke $SNAPSHOT_DIR hari ini. Baris terakhir memperbarui $LATEST_LINK supaya menunjuk ke snapshot yang baru saja selesai dibuat, siap jadi rujukan --link-dest untuk backup berikutnya besok. --delete di sini aman dipakai karena konteksnya berbeda dari kekhawatiran di 21.1.1: yang dihapus cuma entri di dalam satu snapshot spesifik supaya isinya persis mencerminkan kondisi sumber hari itu, sementara snapshot-snapshot hari sebelumnya tetap utuh tidak tersentuh sama sekali.

Untuk membuktikan hard link ini benar-benar menghemat ruang, bandingkan ukuran tiap folder snapshot yang dicek satu per satu dengan ukuran gabungan seluruh folder backup.

du -sh "$BACKUP_ROOT/2026-08-30"
du -sh "$BACKUP_ROOT/2026-08-31"
du -sh "$BACKUP_ROOT"

Dicek satu per satu lewat perintah terpisah seperti ini, tiap folder snapshot menunjukkan ukuran seolah masing-masing berdiri sendiri secara penuh. Tapi dijalankan sekali untuk seluruh $BACKUP_ROOT, GNU du secara default cuma menghitung data yang sama satu kali meski dirujuk oleh banyak hard link sekaligus, sesuai dokumentasi resmi GNU Coreutils, sehingga totalnya jauh lebih kecil dibanding penjumlahan manual ukuran tiap snapshot itu. Kalau totalnya ternyata mendekati jumlah semua snapshot, bukan jauh lebih kecil, kemungkinan --link-dest gagal menemukan file yang cocok untuk di-hard-link, entah karena $LATEST_LINK memang belum ada di percobaan pertama (snapshot pertama wajar tidak punya pembanding), atau karena metadata file di sumber (permission, timestamp) ikut berubah sehingga rsync menganggapnya sebagai file baru yang berbeda.

21.1.3 Arsip tar untuk Backup Sekali Jalan

Untuk kebutuhan berbeda, misalnya snapshot sekali jalan sebelum melakukan perubahan besar atau backup konfigurasi /etc yang jarang berubah, arsip tar tunggal seperti pola 13.2.3 masih lebih praktis dibanding sistem snapshot bertingkat. Konteks server sering menambahkan satu kebutuhan yang jarang muncul di desktop: mengirim arsip itu langsung ke mesin lain tanpa pernah menyentuh disk lokal, memakai tar yang menulis outputnya ke stdout lalu disalurkan lewat pipe ke ssh.

tar -czf - -C /etc . | ssh backup@server-cadangan \
    "cat > /backup/etc-$(hostname)-$(date +%F).tar.gz"

Tanda - pada opsi -f - memberitahu tar untuk menulis arsipnya ke stdout alih-alih ke file, konsisten dengan konvensi "dash berarti stdin/stdout" yang juga berlaku di banyak tool Unix lain. Pipe menyalurkan aliran byte arsip itu langsung menjadi input perintah ssh, yang di sisi server tujuan cuma menjalankan cat untuk menuliskannya ke file. Trik ini menghindari kebutuhan menulis file arsip sementara di server sumber, berguna kalau ruang disk lokal terbatas atau data yang dibackup sangat besar. Sub-bab 21.2 membahas cara transfer file yang lebih umum dan lebih mudah diverifikasi progresnya lewat scp, sftp, dan rsync over SSH.

21.1.4 Rotasi Backup Berlapis

Menyimpan snapshot harian selamanya jelas bukan solusi realistis, disk tetap akan penuh cepat atau lambat meski hemat ruang lewat hard link. Pola rotasi paling umum dipakai Sysadmin adalah rotasi berlapis (kadang disebut skema grandfather-father-son): simpan snapshot harian untuk beberapa hari terakhir, satu snapshot mingguan untuk beberapa minggu terakhir, dan satu snapshot bulanan untuk retensi jangka panjang. Kombinasi ini menyeimbangkan dua kebutuhan yang saling tarik-menarik, yaitu granularitas pemulihan yang tinggi untuk kejadian baru-baru ini dan riwayat jangka panjang tanpa harus menyimpan salinan harian selama bertahun-tahun.

Implementasi paling sederhana cukup memperluas pola find ... -mtime +N -delete dari 13.2.4 untuk tiap kelas retensi, dijalankan lewat cron setelah backup harian selesai.

find "$BACKUP_ROOT" -maxdepth 1 -type d -name "20*" -mtime +7 -exec rm -rf {} \;

-mtime +7 mencocokkan folder snapshot yang sudah lebih dari 7 hari, dan -exec rm -rf {} \; menghapusnya satu per satu. Untuk retensi mingguan atau bulanan, cara paling sederhana adalah menjadwalkan satu job cron terpisah yang hanya berjalan sekali seminggu atau sekali sebulan (bukan menyeleksi snapshot harian mana yang "kebetulan" mingguan), yang menyalin snapshot terbaru ke folder retensi berbeda dengan rentang -mtime yang lebih longgar, misalnya 90 hari untuk mingguan dan 365 hari untuk bulanan. Perintah rm -rf di atas bersifat destruktif dan permanen, jadi seperti biasa, ganti dulu sementara dengan -print untuk melihat daftar folder yang akan terhapus sebelum yakin memakai -exec rm -rf sungguhan di server produksi.

21.2 Transfer File Aman Lewat SSH

Backup yang sudah dibahas di 21.1 baru berguna penuh kalau salinannya juga tersimpan di mesin lain, bukan cuma di disk yang sama dengan data aslinya. Tiga sub-bab ini membahas cara memindahkan file antar server secara aman lewat SSH, sekaligus menyiapkan autentikasi supaya prosesnya bisa berjalan otomatis tanpa ada yang mengetik password.

21.2.1 Autentikasi SSH Key untuk Script Otomatis

Script yang dijadwalkan lewat cron tidak punya cara mengetikkan password saat diminta, jadi autentikasi berbasis password sama sekali tidak cocok untuk automasi. Solusinya adalah public key authentication: kita membuat sepasang key, menyimpan bagian privatnya di mesin sumber, lalu mendaftarkan bagian publiknya ke mesin tujuan, sehingga ssh bisa membuktikan identitas tanpa password sama sekali.

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_deploy -N "" -C "deploy@$(hostname)"

-t ed25519 memilih algoritma Ed25519, direkomendasikan dokumentasi resmi OpenSSH sebagai pilihan modern menggantikan RSA karena key-nya jauh lebih pendek dengan tingkat keamanan setara. -f menentukan nama file key, dan -N "" mengisi passphrase kosong, wajib untuk key yang dipakai script otomatis karena tidak ada manusia yang bisa mengetikkan passphrase saat cron memanggilnya tengah malam. Perlu jujur diakui, private key tanpa passphrase ini jadi satu-satunya lapisan pertahanan; siapa pun yang berhasil mencurinya bisa langsung menyamar sebagai script itu. Bab 23 membahas lebih dalam cara membatasi risiko ini, misalnya lewat opsi command= di authorized_keys yang memaksa key tertentu cuma bisa menjalankan satu perintah spesifik, tidak bebas membuka shell interaktif.

ssh-copy-id -i ~/.ssh/id_ed25519_deploy.pub deploy@server-tujuan

ssh-copy-id menyalin isi key publik ke ~/.ssh/authorized_keys milik user deploy di server tujuan lewat satu kali autentikasi password (percobaan terakhir sebelum password tidak dibutuhkan lagi). sshd menolak autentikasi key kalau permission direktori ~/.ssh atau file authorized_keys di sisi server terlalu longgar (grup atau pihak lain punya akses tulis), sesuai pemeriksaan StrictModes yang aktif secara default menurut man sshd_config; kebiasaan aman yang berlaku luas adalah chmod 700 ~/.ssh dan chmod 600 ~/.ssh/authorized_keys maupun file private key itu sendiri. Uji hasilnya sebelum lanjut ke langkah berikutnya.

ssh -i ~/.ssh/id_ed25519_deploy deploy@server-tujuan 'echo koneksi berhasil'

Kalau yang muncul justru error Permission denied (publickey), dua penyebab paling umum di lapangan adalah permission ~/.ssh/authorized_keys di server tujuan yang belum sesuai seperti dijelaskan di atas, atau isi key publik yang tersalin tidak lengkap (terpotong saat disalin manual, bukan lewat ssh-copy-id). Tambahkan opsi -v pada perintah ssh di atas untuk menampilkan proses autentikasi langkah demi langkah, termasuk key mana saja yang dicoba dan alasan penolakannya, jauh lebih cepat dibanding menebak-nebak penyebabnya.

21.2.2 Salin File dengan scp

scp menyalin file atau folder lewat SSH dengan sintaks yang mirip cp biasa, cocok untuk transfer sekali jalan yang tidak butuh sinkronisasi bolak-balik.

scp -i ~/.ssh/id_ed25519_deploy -P 2222 -r ./dist/ deploy@server-tujuan:/var/www/aplikasi/

-r menyalin folder secara rekursif, persis seperti pada cp. Satu jebakan yang sering bikin bingung: opsi port pada scp (dan sftp) memakai huruf besar -P, berbeda dengan ssh yang memakai huruf kecil -p, karena pada scp huruf kecil -p sudah dipakai lebih dulu untuk arti lain, yaitu mempertahankan waktu modifikasi, waktu akses, dan mode permission file asli, sesuai man scp. Salah menukar huruf besar-kecil ini kadang tidak langsung terlihat errornya, terutama kalau server tujuan kebetulan memakai port SSH default 22.

21.2.3 Transfer Batch dengan sftp

sftp membuka sesi transfer file interaktif lewat SSH, mirip FTP klasik tapi seluruh koneksinya sudah terenkripsi karena berjalan di atas protokol SSH. Untuk dipakai di script otomatis, sesi interaktifnya perlu diganti mode batch, membaca daftar perintah dari file atau heredoc alih-alih menunggu ketikan manual.

sftp -i ~/.ssh/id_ed25519_deploy -P 2222 deploy@server-tujuan <<EOF
put dist.tar.gz /var/www/uploads/
ls /var/www/uploads/
bye
EOF

Perintah put mengunggah file dari mesin lokal ke server tujuan, get melakukan kebalikannya, dan bye menutup sesi. Opsi -b file_batch menjadi alternatif kalau daftar perintahnya lebih panjang dan ingin disimpan terpisah dari script utama, dibaca persis seperti heredoc di atas tapi dari file, sesuai man sftp. sftp lebih cocok dipakai ketika transfer perlu diselingi perintah lain di sisi remote seperti ls atau rename dalam satu sesi, sesuatu yang tidak bisa dilakukan scp yang sifatnya sekali salin lalu selesai.

21.2.4 Sinkronisasi Efisien dengan rsync over SSH

Untuk transfer berulang antar server, misalnya menyalin hasil build ke banyak server aplikasi tiap deploy, rsync lewat SSH jauh lebih efisien dibanding scp karena cuma mengirim bagian yang berubah, persis alasan yang sama dengan 13.2.1 tapi kali ini lewat jaringan, bukan cuma antar folder lokal.

rsync -avz -e "ssh -i ~/.ssh/id_ed25519_deploy -p 2222" \
    ./dist/ deploy@server-tujuan:/var/www/aplikasi/

-e menentukan perintah remote shell yang dipakai rsync untuk membuka koneksi, di sini ssh lengkap dengan key dan port kustomnya; perlu diperhatikan, opsi port di dalam perintah ssh yang diteruskan lewat -e ini kembali memakai huruf kecil -p, bukan huruf besar seperti pada scp/sftp, karena yang dipanggil di sini benar-benar ssh, bukan scp. Opsi -z mengompresi data selama transfer, berguna kalau jaringan antar server jadi bottleneck (misalnya lewat WAN), tapi kurang berguna atau bahkan memperlambat kalau data yang ditransfer sudah terkompresi sebelumnya (arsip .tar.gz, gambar, video) atau kalau kedua server berada di jaringan lokal yang sudah sangat cepat.

Satu hal penting yang perlu dibedakan menurut dokumentasi resmi rsync: sintaks path tujuan dengan satu titik dua (user@host:/path) seperti contoh di atas memakai transport remote shell (SSH), sedangkan dua titik dua (host::modul/path) atau URL berawalan rsync:// memakai protokol rsync daemon yang terhubung langsung ke port 873 tanpa enkripsi SSH sama sekali, kecuali daemon itu sendiri sengaja di-tunnel lewat SSH terpisah. Untuk kebutuhan transfer antar server produksi, sintaks satu titik dua di atas jauh lebih aman karena otomatis terenkripsi lewat SSH, dan itu sebabnya seluruh contoh di bab ini konsisten memakainya.

ToolKapan DipakaiKekurangan
scpSalin sekali jalan, file/folder tunggal, tidak butuh sinkronisasiMenyalin ulang seluruhnya tiap kali, tidak efisien untuk transfer berulang
sftpSesi transfer interaktif atau butuh perintah remote tambahan (list, rename)Mode batch sedikit lebih rumit disiapkan dibanding satu baris scp
rsync over SSHSinkronisasi berulang, hanya bagian berubah yang perlu dikirim ulangSintaks sedikit lebih kompleks, butuh rsync terpasang di kedua sisi

21.3 Script Deployment Sederhana

Dua sub-bab berikut membangun script deployment paling dasar: menarik kode terbaru dan me-restart service, ditutup mekanisme rollback untuk kondisi ketika deployment ternyata gagal.

21.3.1 Alur Deployment Pull-Based

Ada dua pola dasar deployment: push-based, di mana mesin lain (laptop Developer atau server CI/CD) yang aktif mengirim kode ke server tujuan, dan pull-based, di mana server itu sendiri yang aktif menarik kode terbaru dari repository pusat. Bab ini fokus ke pola pull-based karena lebih sederhana diimplementasikan murni pakai Bash tanpa infrastruktur tambahan: server sudah punya satu clone repository yang di-setup sekali di awal, dan script deployment cukup menjalankan git fetch lalu git reset --hard ke commit terbaru dari branch yang dipantau. Sintaks git yang dipakai di sini sengaja dijaga minimal; pembahasan lebih dalam soal versioning dan alur kerja Git ada di Bab 26.3.

git reset --hard bersifat destruktif: seluruh perubahan lokal yang belum ter-commit di folder itu akan hilang permanen begitu perintah ini dijalankan, sesuai dokumentasi resmi Git. Konsekuensi praktisnya, folder aplikasi di server produksi tidak boleh pernah diedit langsung manual (misalnya menambal satu baris kode buru-buru lewat vim saat ada bug mendesak), karena tambalan itu akan lenyap begitu saja di deployment berikutnya. Semua perubahan, sekecil apa pun, wajib lewat repository. Ini bukan sekadar soal kerapian, tapi konsekuensi langsung dari cara kerja pola pull-based itu sendiri.

21.3.2 Implementasi deploy.sh

Service yang di-restart di dalam script butuh hak root, tapi menjalankan seluruh script deployment sebagai root lewat sudo bash deploy.sh memberi kekuasaan jauh lebih besar dari yang dibutuhkan. Cara yang lebih aman: buat user deploy khusus (Bab 17.3), lalu izinkan lewat visudo supaya user itu cuma boleh menjalankan satu perintah spesifik tanpa password.

# Baris tambahan di /etc/sudoers.d/deploy (edit lewat visudo -f)
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart aplikasi-web.service

Menuliskan path lengkap /usr/bin/systemctl beserta nama unit spesifik aplikasi-web.service, bukan sekadar systemctl restart *, penting supaya izin ini tidak bisa disalahgunakan untuk me-restart atau menghentikan service lain yang tidak seharusnya bisa disentuh user deploy. Bab 23 membahas lebih jauh prinsip least privilege semacam ini untuk hardening server.

#!/bin/bash
set -euo pipefail

APP_DIR="/var/www/aplikasi"
SERVICE="aplikasi-web.service"
BRANCH="main"
LOG_FILE="/var/log/deploy.log"

log() {
    printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE"
}

cd "$APP_DIR"
log "Mulai deploy branch $BRANCH"

git fetch origin "$BRANCH"
git reset --hard "origin/$BRANCH"
log "Kode terbaru ditarik, commit: $(git rev-parse --short HEAD)"

sudo systemctl restart "$SERVICE"
sleep 2

if systemctl is-active --quiet "$SERVICE"; then
    log "Deploy berhasil, $SERVICE aktif"
else
    log "Deploy GAGAL, $SERVICE tidak aktif setelah restart"
    exit 1
fi

set -euo pipefail di baris awal mengaktifkan mode debug yang dibahas tuntas di Bab 25.1; berbeda dengan script alert di 20.4.2 yang sengaja tidak memakai -e, script deployment ini justru perlu langsung berhenti begitu satu langkah gagal (misalnya git fetch gagal karena jaringan bermasalah), karena melanjutkan ke langkah restart service dengan kode yang gagal ditarik sepenuhnya jauh lebih berbahaya dibanding sekadar berhenti dan melapor gagal. systemctl is-active --quiet, pola yang sama dari Bab 19.3.2, dipakai untuk memverifikasi service benar-benar aktif setelah restart, bukan cuma berasumsi restart pasti berhasil begitu perintahnya selesai dijalankan.

21.3.3 Rollback Otomatis Saat Deployment Gagal

Cara paling sederhana mengimplementasikan rollback di pola pull-based ini adalah menyimpan commit hash yang sedang aktif sebelum menarik kode baru, supaya ada titik pasti untuk kembali kalau ternyata diperlukan.

PREV_COMMIT=$(git rev-parse HEAD)

git fetch origin "$BRANCH"
git reset --hard "origin/$BRANCH"

sudo systemctl restart "$SERVICE"
sleep 2

if ! systemctl is-active --quiet "$SERVICE"; then
    log "Deploy gagal, rollback ke commit $PREV_COMMIT"
    git reset --hard "$PREV_COMMIT"
    sudo systemctl restart "$SERVICE"
    exit 1
fi

Pola ini cukup untuk aplikasi kecil hingga menengah yang seluruh state pentingnya ada di database terpisah, bukan di folder kode itu sendiri. Untuk aplikasi berskala lebih besar dengan banyak dependency yang ikut berubah tiap rilis, pola yang lebih matang biasanya memakai folder releases/ bernomor timestamp dan satu symbolic link current yang dialihkan ke folder rilis mana pun secara instan (mirip pola yang dipopulerkan tool deployment seperti Capistrano); rollback di pola itu cuma soal mengarahkan ulang symbolic link tanpa perlu git reset sama sekali. Pola commit hash di atas dipilih di bab ini karena cukup untuk kebutuhan Bash murni tanpa tool tambahan, sekaligus jadi fondasi yang mudah dipahami sebelum naik ke pola yang lebih kompleks itu.

21.4 Studi Kasus: Pipeline Deploy Manual Berbasis Bash

Studi kasus penutup ini menyatukan tiga topik bab ini jadi satu alur: backup snapshot sebelum deploy (21.1), lalu deploy dengan rollback (21.3), dan notifikasi otomatis (memakai ulang pola kirim_alert() dari 20.4.2) begitu ada langkah yang gagal.

21.4.1 Rancangan Pipeline End-to-End

Urutan langkah pipeline deploy-pipeline.sh: (1) simpan snapshot kondisi folder aplikasi saat ini lewat rsync --link-dest, sebagai jaring pengaman terpisah dari rollback berbasis Git; (2) tarik kode terbaru; (3) restart service; (4) jalankan health check sederhana lewat curl ke endpoint aplikasi, bukan cuma mengecek service aktif seperti 21.3.2, karena service bisa saja "aktif" menurut systemd tapi aplikasinya sendiri gagal merespons permintaan; (5) kalau health check gagal, rollback ke commit sebelumnya dan kirim notifikasi. Bab 22 nanti membahas health check jaringan secara lebih menyeluruh; pemakaian curl di sini sengaja dijaga sesederhana mungkin, cukup untuk kebutuhan pipeline deploy.

21.4.2 Implementasi deploy-pipeline.sh

#!/bin/bash
set -uo pipefail

APP_DIR="/var/www/aplikasi"
SERVICE="aplikasi-web.service"
BRANCH="main"
BACKUP_ROOT="/backup/aplikasi"
HEALTHCHECK_URL="http://127.0.0.1:8080/health"
LOG_FILE="/var/log/deploy-pipeline.log"

source /etc/deploy-pipeline/alert-notifikasi.env

log() {
    printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE"
}

kirim_alert() {
    local pesan="$1"
    log "ALERT: $pesan"
    [[ -z "${TELEGRAM_BOT_TOKEN:-}" ]] && return 0
    curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
        -d chat_id="$TELEGRAM_CHAT_ID" \
        --data-urlencode "text=$pesan" > /dev/null
}

cd "$APP_DIR"
PREV_COMMIT=$(git rev-parse HEAD)
SNAPSHOT_DIR="$BACKUP_ROOT/$(date +%Y-%m-%d-%H%M%S)"

log "Simpan snapshot sebelum deploy"
rsync -a --delete --link-dest="$BACKUP_ROOT/latest" "$APP_DIR/" "$SNAPSHOT_DIR/"
rm -f "$BACKUP_ROOT/latest"
ln -s "$SNAPSHOT_DIR" "$BACKUP_ROOT/latest"

log "Tarik kode branch $BRANCH"
if ! git fetch origin "$BRANCH" || ! git reset --hard "origin/$BRANCH"; then
    kirim_alert "Deploy $(hostname) gagal: git pull bermasalah"
    exit 1
fi

log "Restart $SERVICE"
sudo systemctl restart "$SERVICE"
sleep 3

if curl -sf -o /dev/null "$HEALTHCHECK_URL"; then
    log "Deploy berhasil, commit $(git rev-parse --short HEAD), health check OK"
else
    log "Health check GAGAL, rollback ke $PREV_COMMIT"
    git reset --hard "$PREV_COMMIT"
    sudo systemctl restart "$SERVICE"
    kirim_alert "Deploy $(hostname) di-rollback otomatis, health check gagal setelah update kode"
    exit 1
fi

kirim_alert() di sini sengaja disederhanakan hanya memakai satu saluran (Telegram) alih-alih dispatcher lengkap tiga saluran di 20.4.2, supaya fokus pembahasan tetap di alur deploy, bukan mengulang isi bab sebelumnya; di server produksi sungguhan, cukup memakai kembali fungsi kirim_alert() lengkap dari 20.4.2 apa adanya. curl -sf -o /dev/null memakai pola berbeda dari HTTP_CODE di 20.2.4: opsi -f (fail) membuat curl sendiri yang mengembalikan exit status bukan nol kalau response HTTP-nya termasuk kode error (4xx atau 5xx), jadi cukup dicek lewat if langsung tanpa perlu menampung kode statusnya secara eksplisit ke variabel, cocok dipakai di sini karena isi response body-nya sendiri tidak dibutuhkan. set -uo pipefail dipakai tanpa -e, sama seperti alasan di 20.4.2, karena logika rollback di bagian akhir sengaja butuh tetap berjalan sekalipun langkah restart pertama di atasnya "gagal" secara exit status.

21.4.3 Verifikasi dan Uji Skenario Gagal

Jalankan dulu manual dan pastikan jalur normal (deploy berhasil, health check lolos) bekerja sesuai harapan sebelum menguji jalur gagalnya.

chmod +x deploy-pipeline.sh
./deploy-pipeline.sh
tail -f /var/log/deploy-pipeline.log

Untuk menguji jalur rollback, sengaja arahkan HEALTHCHECK_URL sementara ke port yang salah atau hentikan service secara manual tepat sebelum menjalankan script, lalu pastikan log menunjukkan git reset --hard "$PREV_COMMIT" benar-benar terpanggil dan notifikasi Telegram benar-benar masuk, bukan cuma tercatat di log. Setelah yakin kedua jalur berjalan benar, kembalikan HEALTHCHECK_URL ke nilai semula. Pipeline ini masih dipicu manual; menyambungkannya ke sistem CI/CD supaya berjalan otomatis setiap kode baru di-push ke branch main dibahas lebih lanjut di Bab 24.1.

Bab ini melengkapi kemampuan Sysadmin memindahkan data dan kode secara aman antar server, dari backup bertingkat sampai deployment dengan jaring pengaman rollback. Bab 22 melanjutkan ke topik jaringan dasar, termasuk pembahasan curl dan health check yang lebih menyeluruh dibanding pemakaian sederhana di 21.4, ditambah tool seperti ss/netstat dan ping untuk mendiagnosis masalah konektivitas yang sering jadi penyebab gagalnya proses deploy maupun transfer file yang sudah dibahas di sini.