Men-deploy Layanan Nyata

Men-deploy aplikasi lengkap dengan unit layanan dan firewall.

Daftar isi

Topik ini mengerjakan tahap kedua proyek, yaitu menempatkan aplikasi nyata di atas server yang sudah disiapkan. Aplikasinya adalah catatan berbasis berkas beserta layanan pencatat status yang menuliskan kondisi aplikasi ke log, lengkap dengan berkas unit systemd dan keputusan firewallnya. Seluruh bagian aplikasi dan layanannya dibangun serta dijalankan nyata di lingkungan latihan, sedangkan pemasangan systemctl dan ufw ditampilkan sebagai pola perintah server yang siap diulang pada VPS hasil penyiapan.

Aplikasi catatan berbasis berkas#

Catatan disimpan sebagai satu berkas teks dengan satu catatan per baris, sehingga seluruh kondisi aplikasi terbaca dengan perintah baca berkas biasa. Skrip catatan.sh menyediakan dua subperintah, yaitu tambah untuk menulis baris baru dan daftar untuk menampilkan isi berkas secara bernomor. Lokasi pustaka, yaitu folder tempat catatan disimpan, tidak ditulis tetap di dalam skrip, melainkan dibaca dari variabel lingkungan PUSTAKA dengan nilai default di bawah /tmp agar dapat langsung dicoba:

bash catatan.sh
#!/usr/bin/env bash
# catatan.sh: aplikasi catatan berbasis berkas
# Pemakaian: catatan.sh tambah "teks catatan" | catatan.sh daftar
set -euo pipefail

PUSTAKA="${PUSTAKA:-/tmp/kerja-deploy-layanan/pustaka}"
BERKAS="$PUSTAKA/catatan.txt"

case "${1:-}" in
    tambah)
        teks="${2:-}"
        if [ -z "$teks" ]; then
            echo "Error: teks catatan kosong." >&2
            exit 1
        fi
        mkdir -p "$PUSTAKA"
        echo "$teks" >> "$BERKAS"
        nomor="$(wc -l < "$BERKAS")"
        echo "catatan #$nomor tersimpan"
        ;;
    daftar)
        if [ ! -f "$BERKAS" ]; then
            echo "pustaka masih kosong"
        else
            awk '{print NR") " $0}' "$BERKAS"
        fi
        ;;
    *)
        echo "Pemakaian: catatan.sh tambah \"teks\" | catatan.sh daftar"
        ;;
esac
text Keluaran
Pemakaian: catatan.sh tambah "teks" | catatan.sh daftar

Dipanggil tanpa argumen, skrip hanya menampilkan cara pemakaiannya dan tidak menjalankan apa pun, sebagaimana keluaran di atas. Blok berikut menambahkan dua catatan lalu menampilkannya. Percabangan case di dalam skrip memilih subperintah, sedangkan nomor catatan dihitung dari jumlah baris berkas:

bash
# folder kerja bersih untuk pustaka aplikasi
rm -rf /tmp/kerja-deploy-layanan
./catatan.sh tambah "beli kopi"
./catatan.sh tambah "rapat pukul sepuluh"
# daftar catatan yang tersimpan
./catatan.sh daftar
text Keluaran
catatan #1 tersimpan
catatan #2 tersimpan
1) beli kopi
2) rapat pukul sepuluh

Dua hal terlihat dari keluaran tersebut. Direktori pustaka dibuat sendiri oleh skrip lewat mkdir -p saat catatan pertama ditulis, dan daftar menampilkan dua baris sesuai isi berkasnya. Format satu baris per catatan memang sengaja sederhana, tetapi polanya sama dengan aplikasi server sungguhan, yaitu data terpisah dari kode dan lokasinya dikendalikan variabel lingkungan.

Layanan pencatat status#

Layanan pencatat status adalah program yang berjalan terus dan setiap interval menulis satu baris kondisi aplikasi ke berkas log. Setiap barisnya memuat waktu pencatatan, status, dan jumlah catatan terkini, sehingga riwayat aplikasi dapat direkonstruksi dari lognya semata. Skrip pencatat-status.sh berikut mengerjakannya. Argumen --layanan menandai bahwa skrip benar-benar dijalankan sebagai layanan, sedangkan tanpa argumen itu skrip hanya menampilkan pemakaiannya:

bash pencatat-status.sh
#!/usr/bin/env bash
# pencatat-status.sh: layanan kecil pencatat status aplikasi catatan
# Pemakaian: pencatat-status.sh --layanan (unit systemd, atau uji dengan BATAS)
set -euo pipefail

PUSTAKA="${PUSTAKA:-/tmp/kerja-deploy-layanan/pustaka}"
JEDA="${JEDA:-1}"
BATAS="${BATAS:-0}"

if [ "${1:-}" = "--layanan" ]; then
    LOG="$PUSTAKA/log/status.log"
    mkdir -p "$(dirname "$LOG")"
    i=0
    while :; do
        if [ -f "$PUSTAKA/catatan.txt" ]; then
            ISI="$(wc -l < "$PUSTAKA/catatan.txt")"
        else
            ISI=0
        fi
        echo "$(date '+%Y-%m-%d %H:%M:%S') status=OK catatan=$ISI" >> "$LOG"
        i=$((i + 1))
        if [ "$BATAS" -gt 0 ] && [ "$i" -ge "$BATAS" ]; then
            break
        fi
        sleep "$JEDA"
    done
    echo "pencatat-status selesai setelah $i pembacaan"
else
    echo "Pemakaian: pencatat-status.sh --layanan"
fi
text Keluaran
Pemakaian: pencatat-status.sh --layanan

Uji berikut membuktikan mode layanan itu benar-benar berjalan, yaitu dipanggil dengan batas tiga pembacaan, lalu lognya dibaca:

bash
# mode layanan dijalankan tiga kali pembacaan
BATAS=3 JEDA=1 ./pencatat-status.sh --layanan
text Keluaran
pencatat-status selesai setelah 3 pembacaan
bash
# isi log tanpa kolom waktu, karena nilainya berbeda setiap eksekusi
cut -d' ' -f3- /tmp/kerja-deploy-layanan/pustaka/log/status.log
text Keluaran
status=OK catatan=2
status=OK catatan=2
status=OK catatan=2

Kolom waktu sengaja dipotong oleh cut karena nilainya adalah tanggal dan jam nyata ketika baris ditulis, berbeda pada setiap eksekusi, sedangkan dua kolom sisanya selalu sama untuk keadaan yang sama. Perulangan while di dalam skrip menjalankan tiga pekerjaan pada setiap putarannya, yaitu menghitung baris catatan, menulis baris log, lalu tidur selama JEDA. Setelah pembacaan ketiga, satu catatan baru ditambahkan dan layanan dijalankan lagi, sehingga lognya ikut membesar:

bash
# catatan ketiga, lalu dua pembacaan berikutnya
./catatan.sh tambah "tinjau laporan"
BATAS=2 JEDA=1 ./pencatat-status.sh --layanan
# jumlah seluruh baris log
grep -c 'status=OK' /tmp/kerja-deploy-layanan/pustaka/log/status.log
text Keluaran
catatan #3 tersimpan
pencatat-status selesai setelah 2 pembacaan
5

Nilai catatan pada baris log naik dari dua menjadi tiga setelah catatan baru masuk, dan penghitung grep memastikan kelima barisnya seluruhnya berstatus OK. Log yang tumbuh seperti ini adalah cara termurah untuk memeriksa bahwa layanan benar-benar berjalan, baik di lingkungan latihan maupun di server.

Berkas unit catatan-layanan#

Berkas unit mengikat skrip pencatat-status.sh ke systemd, sehingga layanannya hidup sejak boot, dihidupkan ulang saat error, dan lognya tercatat di jurnal. Bentuknya seperti berikut:

ini catatan-layanan.service
[Unit]
Description=Layanan pencatat status aplikasi catatan

[Service]
Type=simple
Environment=PUSTAKA=/var/lib/catatan
Environment=JEDA=60
ExecStart=/usr/local/bin/pencatat-status.sh --layanan
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Setiap barisnya berperan sebagai berikut:

  • Bagian [Unit] hanya memuat Description=, yaitu keterangan yang tampil pada systemctl status dan daftar unit.
  • Type=simple menyatakan layanan dianggap berjalan begitu prosesnya hidup, cocok untuk skrip yang tidak melepas diri ke latar belakang, berbeda dari Type=oneshot untuk tugas sekali jalan.
  • Dua baris Environment= mengganti konfigurasi skrip tanpa menyuntingnya, yaitu pustaka dipindah ke /var/lib/catatan dan interval diperbesar menjadi 60 detik.
  • ExecStart= memanggil skrip dengan jalur absolut beserta argumen --layanan, sehingga yang dijalankan systemd adalah mode layanannya, bukan pesan pemakaiannya.
  • Restart=on-failure dengan RestartSec=5 meminta systemd menghidupkan ulang layanan yang berakhir karena error, dengan jeda lima detik.
  • Bagian [Install] menautkan unit ke multi-user.target saat di-enable, yaitu target standar server tanpa antarmuka grafis.

Pemeriksaan systemd-analyze verify berlaku sama untuk berkas ini sebelum dipasang, sebagaimana berkas unit lain. Penyebut /var/lib/catatan dipilih karena /var adalah wilayah data yang tumbuh saat server berjalan, sesuai pembagian hierarki sistem berkas.

Pemasangan dan keputusan firewall#

Pemasangan mengikuti urutan tetap, yaitu menyalin skrip ke /usr/local/bin, menyiapkan direktori pustakanya, menyalin berkas unit, memuat ulang daftar unit, lalu mengaktifkan layanannya:

bash
# tidak-dijalankan
# pemasangan menuntut hak root dan systemd yang berjalan
sudo cp pencatat-status.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/pencatat-status.sh
sudo mkdir -p /var/lib/catatan
sudo cp catatan-layanan.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now catatan-layanan.service

Setelah aktif, keadaan layanannya diperiksa dengan systemctl status, dan baris log terbaca dari jurnal tanpa perlu mencari berkas lognya. Baris enable --now menggabungkan dua pekerjaan, yaitu menautkan unit agar aktif saat boot dan menjalankannya saat itu juga, sehingga layanan langsung tampak pada status tanpa perintah start terpisah:

bash
# tidak-dijalankan
# log layanan tercatat di jurnal systemd
systemctl status catatan-layanan.service --no-pager
journalctl -u catatan-layanan.service -n 20

Keputusan firewallnya mengikuti arsitektur proyek ini, yaitu aplikasi catatan tidak mendengarkan port publik, sehingga tidak ada aturan ufw baru yang diperlukan dan satu-satunya jalur masuk tetap SSH. Apabila aplikasi yang dideploy mendengarkan port, misalnya 8080, aturannya ditambahkan sebelum firewall diaktifkan:

bash
# tidak-dijalankan
# untuk aplikasi yang mendengarkan port: izinkan portnya sebelum enable
sudo ufw allow 8080/tcp
# verifikasi keadaan firewall setiap kali layanan baru dipasang
sudo ufw status verbose

Pola yang ditegaskan di bagian ini adalah memeriksa daftar port yang mendengarkan sebelum dan sesudah deploy, sehingga tidak ada port baru yang terbuka tanpa diketahui. Perintah ss -tln menampilkan daftar tersebut, dan pemeriksaannya diulang setelah layanan diaktifkan. Layanan tanpa port publik seperti aplikasi catatan ini memang tidak menambah permukaan serangan, dan firewall yang tidak berubah adalah hasil deploy yang wajar untuknya.

Kesalahan umum#

  • Menjalankan ./pencatat-status.sh --layanan tanpa BATAS di terminal lalu mengira terminal macet. Mode layanannya memang dirancang berjalan tanpa akhir, sehingga uji manual selalu menyertakan BATAS atau dihentikan lewat Ctrl-C.
  • Menyalin skrip ke /usr/local/bin tanpa izin eksekusi. systemd menemukan berkasnya tetapi gagal mengeksekusinya, dan layanan berakhir dengan status 203 berulang kali sesuai aturan Restart.
  • Memanggil systemctl start sebelum daemon-reload. systemd belum mengenali unit baru yang barusan disalin, sehingga perintahnya berakhir dengan pesan unit tidak ditemukan.
  • Mengira aplikasi otomatis terjangkau dari luar setelah enable. systemd hanya menjalankan prosesnya, sedangkan jangkauan ditentukan firewall, dan aplikasi tanpa port publik memang tidak dibukakan aturan apa pun.

Ringkasan#

  • Aplikasi catatan menyimpan datanya sebagai satu berkas teks, dengan lokasi dikendalikan variabel lingkungan PUSTAKA.
  • pencatat-status.sh menulis satu baris log setiap interval, argumen --layanan menandai mode layanannya, dan variabel BATAS membatasi pembacaan saat uji.
  • Berkas unit memakai Type=simple, mengonfigurasi skrip lewat Environment=, dan menetapkan Restart=on-failure agar error tidak mematikan layanan secara permanen.
  • Aplikasi tanpa port publik tidak menambah aturan ufw, sedangkan port baru selalu diizinkan sebelum firewall diaktifkan.

Referensi#