Skip to main content

Bash | Learning Bash/Shell Scripting The Hard Way

Meskipun saya sudah menggunakan Sistem Operasi berbasis Linux sejak lebih dari 8 tahun lalu.
Cukup jarang saya harus berkutat dengan bash/shell scripting/programming, saya hanya sekedar tahu dan tidak pernah belajar lebih dalam.

Tapi karena tuntutan profesi (*haha profesi jare), mau tidak mau saya harus sedikit belajar, dengan bekal script warisan dari SysAdmin sebelumnya.

Langsung saja studi kasusnya adalah membuat bash script untuk melakukan proses backup database sistem (kami menggunakan PostgreSQL)  ke layanan Google Drive (berikutnya kita sebut gdrive). Kami mempunyai beberapa database server dan beberapa diantaranya dibatasi hanya bisa diakses dan mengakses via jaringan lokal (private network).

Untuk keperluan komunikasi dengan Google Drive via terminal, saya menggunakan tool bernama drive yang merupakan project open source dan bisa diakses via https://github.com/odeke-em/drive.

Gambaran untuk prosesnya adalah seperti ini:
Di node backup masih ada file backup yang lama untuk keperluan menghapusnya dari gdrive dan menggantinya dengan file backup baru. Lalu proses backup dilakukan, sehingga akan ada file backup baru dan backup lama di dalam satu folder. Langkah selanjutnya adalah menghapus file backup lama dari gdrive kemudian mengupload file backup yang baru. File backup yang lama kemudian dihapus dari node backup. Dan yang terakhir, untuk menghemat space di node backup, content file backup yang baru akan direset, sebab proses delete dari gdrive kita cukup mengetahui nama filenya saja.  


Berikut detail langkah yang saya lakukan
  1. Buat bash script di node yang bertugas untuk menjalankan backup (berikutnya kita sebut node backup), tentunya merupakan node yang bisa mengakses internet untuk keperluan upload ke gdrive
    • backup database postgres dan lakukan kompresi untuk menghemat space (di node backup maupun di gdrive nantinya) dan meringankan proses transfer dari node database ke node backup (meskipun proses ini melalui private network yang tentu jauh lebih cepat dibanding via internet). Perintahnya mudah, tinggal pipe hasil pg_dump ke gzip

      pg_dump -U[user] [nama database] | gzip > $nama_file
    • selanjutnya adalah melakukan file transfer dari node database (karena nantinya proses backup terjadi di node database bukan di node backup, dan karena saya tidak ingin melakukan backup langsung via jaringan *opsi -h [host database]). Saya menggunakan ftp untuk proses transfernya. Perintahnya adalah

      ftp -n -v $HOST_NODE_BACKUP << EOT
      ascii
      user $USERNAME $PASSWORD
      prompt
      # lakukan perintah ftp di sini
      quit
      EOT
  2. Seperti yang saya tulis di langkah ke-1, bahwa script backup ditulis di node backup, namun akan dijalankan di node database, untuk itu kita bisa menulis perintah

    cat script_backup_database.sh | ssh [user@node_database]
  3. Strateginya adalah saya ingin menghapus file backup yang lama dari gdrive dan menggantinya dengan file backup yang baru. Untuk keperluan (drive delete) ini saya perlu tahu nama file backup sebelumnya. Saya mengambil langkah untuk tetap menyimpan file backup yang sudah diupload ke gdrive di node backup, hanya saja saya juga tidak ingin memenuhi space di node backup. Saya hanya perlu tahu nama filenya saja. Jadi ketika proses upload selesai saya mereset content dari file backup dengan perintah

    cat /dev/null > $nama_file_backup*perintah ini ditulis di baris paling akhir dalam script nantinya

    dengan begitu nantinya saya bisa menghapus file dari gdrive dengan perintah

    drive delete -quiet $nama_file_backup_lama 
    *pakai opsi -quiet agar tidak ada prompt Yes/No sebelum menjalan proses aka forcing the process.


    Ketika proses backup database selesai (sebelum diupload ke gdrive), file backup baru maupun file backup lama masih tetap ada di node backup. Jadi ketika harus melakukan proses upload untuk file baru saja dengan perintah

    ls -t [filter file] | head -n 2 | xargs drive push -quiet -destination [folder di gdrive] 2>/dev/null
    *di sini kita bisa belajar kehebatan proses piping (meneruskan output perintah sebelumnya sebagai input ke proses setelahnya) di bash/shell
    • ls -t berfungsi untuk menampilkan list file berurutan mulai dari file terbaru di posisi teratas, tambah filter file yang ingin dilist jika perlu
    • head -n 2 bertugas untuk mengambil n file teratas, dalam kasus ini saya mengambil 2 file teratas (file backup yang terbaru)
    • xargs berfungsi untuk menampung output sebagai argument untuk perintah setelahnya, dalam hal ini,
    • drive push -quiet yang berfungsi untuk mengupload file ke gdrive, dalam kasus ini adalah file yang masuk ke xargs. Opsi -destination bisa kita tambahkan jika ingin file diupload ke folder tertentu, bukan pada folder root dari gdrive. Opsi -quiet sama fungsinya dengan sample drive delete.
    • 2>/dev/null berfungsi untuk meredirect error output ke /dev/null
  4. Setelahnya adalah menghapus file backup yang lama, karena sudah tidak diperlukan lagi. Sebab ketika script dijalankan lagi nantinya, file backup yang baru saja diupload akan berubah statusnya menjadi "file backup lama". Perintah yang dijalankan adalah

    ls -t [filter file] | tail -n +3 | xargs rm 2>/dev/null
    • tail -n +3 adalah kebalikan dari perintah head, yang mengambil n file terbawah, jika ditambahkan opsi +, berarti file terbawah yang diambil dimulai dari index ke-3. Sesuai dengan langkah sebelumnya, file backup terbaru adalah 2 file teratas
    • rm sudah tentunya untuk menghapus file yang ditampung oleh xargs
  5. Terakhir adalah reset content file backup baru dengan perintah yang sudah saya sebutkan di awal langkah ke-3
  6. Tambahan, script backup belum masuk dalam cronjob, maka saya harus mengeksekusi script tersebut secara manual. Namun karena koneksi saya tidak stabil dan tidak mau jika nantinya proses backup tidak bisa terpantau jika koneksi saya putus, maka opsi yang ada adalah mengeksekusi bash script ke background proses. Perintahnya adalah

    nohup file_bash_script.sh &

    Jadi meskipun koneksi bash/terminal saya putus atau saya logout dari bash sekalipun, proses backup akan tetap berjalan. Sehingga ketika login kembali, saya masih bisa memantau proses backup. 


Semoga bermanfaat
Then it's time to take good sleep now ^_^!

TODO:
  1. Memasukan file script ke cronjob
  2. Menambahkan strategi checksum agar kita bisa melakukan kroscek apakah file yang terupload di gdrive sudah valid atau belum

Comments

Popular posts from this blog

Java | Menambahkan Dialog Konfirmasi Sebelum Keluar dari Aplikasi

Kali ini saya ingin berbagi cara sederhana untuk menampilkan dialog konfirmasi sebelum keluar dari aplikasi. Dengan syarat aplikasi yang kita buat harus menggunakan JFrame sebagai frame utama. Langkah-langkah yang harus kita lakukan adalah sebagai berikut: 1. Ganti property default close operation dari JFrame menjadi DO_NOTHING_ON_CLOSE, value ini dimaksudkan agar ketika button close dari JFrame diklik tidak akan terjadi apa-apa. Sebaliknya jika valuenya adalah EXIT_ON_CLOSE, maka ketika button close dari JFrame diklik maka program akan tertutup. setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 2. Yang kedua adalah kita harus menambahkan WindowListener pada JFrame. Interface WindowListener memiliki beberapa method terkait event terhadap window (yakni JFrame), namun yang harus kita override hanyalah method windowClosing, method ini akan dipanggil ketika button close diklik.   Sebagai informasi, apabila kita menambahkan WindowListener secara langsung maka semua method dari i

PostgreSQL | Membuat Nomor Urut dengan Window Function ROW_NUMBER()

Pengguna Oracle Database mungkin sudah sangat familiar dengan clausa ROWNUM. Bagi yang belum tahu apa itu ROWNUM,  ROWNUM  adalah  pseudo column  (kolom bayangan) yang berisi nomor urut dari hasil eksekusi query. Sayangnya fitur ini belum tersedia pada PostgreSQL . Namun ada kabar gembira bagi kita pengguna  PostgreSQL , dimana sejak versi 8.4, PostgreSQL telah menyediakan 1 window function  yang mengakomodasi masalah ROWNUM, yakni ROW_NUMBER() . Sesuai dengan dokumentasi yang disediakan, cara menggunakan fungsi ini adalah dengan menggabungkannya dengan clausa OVER  yang didalamnya berisi clausa ORDER BY . Supaya lebih jelas dan lebih mudah dipahami, lebih baik langsung kita praktekkan saja. Saya membuat sebuah table dengan struktur sebagai berikut CREATE TABLE name ( code CHAR(1) ); yang kemudian saya isi kolom code dengan alfabet mulai dari a s/d z  dan angka 0 (nol). Sebelumnya kita lakukan query ke table name tanpa clausa ORDER BY : SELECT code FROM name; Outputny

PostgreSQL | Ekspresi Kondisional CASE Pada Query

Salah satu fitur yang saya sukai dari PostgreSQL adalah dapat digunakannya ekspresi kondisional pada query. PostgreSQL menyediakan ekpresi CASE yang memiliki fungsi yang sama dengan statemen IF/ELSE pada bahasa pemrograman. Berikut adalah bentuk dari ekspresi CASE pada PostgreSQL CASE WHEN condition THEN result      [WHEN ...]      [ELSE result] END Untuk melihat bagaimana cara menggunakan ekspresi CASE, saya memiliki sebuah tabel number dengan sebuah kolom nomor yang memuat data bertipe integer. SELECT nomor FROM number ORDER BY nomor;  nomor -------      1      3     32     37     38     97 (6 rows) Sekarang mari kita gunakan ekspresi CASE untuk menentukan apakah nomor tersebut merupakan bilangan ganjil atau genap. SELECT nomor, (CASE WHEN nomor % 2 = 0 THEN 'Bilangan Genap' ELSE 'Bilangan Ganjil' END) AS status FROM number ORDER BY nomor; dan hasilnya adalah sebagai berikut:  nomor |     status     -------+-----------------      1 | Bilangan