Mufid's Code blog

Merakit Komputer untuk Masa Depan

| Comments

Artikel stub.

Agar enggak salah untuk upgrade

Motherboard

Tentukan kebutuhan dasar dari komponen utama:

  • Intel / AMD?
  • Form factor? ATX? ITX?

Tentukan kebutuhan tersier dari komponen utama:

  • Butuh yang bisa Wifi? Kalau hanya kabel, kenapa harus Wifi?

Tentukan kebutuhan ekspansi dari komponen yang ada:

  • Jumlah slot RAM?
  • Akankah Crossfire?
  • Jumlah slot M.2?

Prosesor

Di awal bisa beli yang cupu. Cari prosesor murah. Misal di awal beli i3. Tapi pastikan motherboard support sampai i9, jadi di masa depan bisa upgrade

RAM

Di awal beli kapasitas maksimal. Soalnya cari RAM susah di masa depan. Apalagi dengan clock dan timing yang sama.

Storage

Bisa mulai dari manapun. Misal HDD dulu, terus mau RAID, terus beli nvme

RAID untuk Kebutuhan Pribadi

| Comments

RAID

Saya sudah lama menggunakan RAID 1 untuk komputer pribadi saya. Sudah sejak 2017. Empat tahun lebih. Sejauh ini saya luar biasa puas dengan hasilnya. Mengapa saya memilih RAID 1 yang notabene harganya menjadi dua kali lipat?

Hal ini karena komputer merupakan aset yang sangat berharga bagi saya. Melakukan riset dan eksperimen pribadi, bermain, hingga hiburan saya lakukan semuanya di komputer Saya. Jika hard disk rusak, saya tidak bisa menggunakan komputer Saya. Pernah beberapa kali hard disknya rusak. Saya haru melakukan pemasangan ulang Windows dan melakukan pemasangan ulang semua perangkat lunak yang saya butuhkan. Seluruh proses penyelamatan memakan waktu 2 hari lebih! Seluruh akhir pekan saya didgunakan hanya untuk penyelamatan dan pemasangan. Tidak efisien.

Hard Disk saya rusak karena banyak hal. Biasanya karena masalah kelistrikan dan umur. Namun saya mencurigai kerusakannya di kelistrikan. Biasanya, Windows tidak mau boot setelah listrik padam secara tiba-tiba dan komputer sedang dinyalakan. Solusi termudahnya adalah beli UPS, tetapi UPS hanya menyelesaikan masalah hard disk yang rusak karena listrik padam tiba-tiba. Tidak menyelesaikan masalah karena umur atau hal lainnya. RAID Mirroring menyelesaikan masalah “masa iya kedua hard disk rusak berbarengan untuk sektor yang sama.”

Saya menggunakan RST atau Intel Rapid Storage — fungsi RAID bawaan dari perangkat keras yang saya miliki. Pertimbangannya saya menggunakan perangkat keras RAID adalah sebagai berikut:

  • Dukungan driver native dari untuk Windows Vista ke atas
  • Tidak perlu konfigurasi apapun di perangkat lunak dan OS
  • Instalasi yang mudah, hanya perlu konfigurasi di BIOS

Adapun salah satu hal yang kurang menyenangkan dalam penggunaan RAID ini adalah kewajiban untuk menjalan RAID verification saat listrik padam tiba-tiba. Pada hard disk dengan kapasitas 3 TB yang saya miliki, proses verifikasi membutuhkan waktu hingga delapan jam lebih! Meski bisa kita lewati, proses verifikasi sudah terjadi saat driver dimuat. Artinya, proses verifikasi sudah dimulai sejak Windows Boot. Jadi, ketika listrik padam, penghidupan komputer selanjutnya akan memperlambat proses boot. Hal ini karena boot, startup, dan verifikasi RAID terjadi bersamaan. Kita baru bisa mengabaikan proses verifikasi ketika menjalankan UI Intel Rapid Storage — yaitu saat sudah login.

Kekurangan lainnya adalah tidak bisa menambah array dengan mudah. Um, ini bukan hanya masalah RAID, sih. Ini masalah yang akan dihadapi kalau kita memiliki lebih dari satu disk. Tanpa RAID pun, menambah disk berarti sudah menambah mount point.

RAID

Tentu saja untuk build selanjutnya saya akan menggunakan RAID 1 lagi. Mungkin juga saya akan pertimbangkan RAID 10. Yang pasti mirroring ini membantu kehidupan saya sekali. Tidak perlu takut bad sector dan hard disk mati tiba-tiba.

Saya tidak mempertimbangkan RAID 5 atau RAID 6 karena terlalu boros untuk pengguna rumahan seperti Saya. Dua disk saja sudah terlalu banyak, apalagi 3 disk atau malah 4 disk. Memang RAID 5 dan RAID 6 menawarkan kecepatan yang lebih tinggi. Akan tetapi, saya akan memilih M.2 SSD jika alasannya adalah kecepatan. Untuk availability yang tinggi seperti RAID 1, kita bisa menggunakan skenario sebagai berikut:

  • SSD digunakan untuk sistem
  • Setup RAID 1 untuk 2 disk
  • Backup full SSD ke disk di RAID 1
  • Andai SSD rusak tiba-tiba, tinggal restore image dari disk di RAID 1 ke SSD.

Bagaimana dengan NAS?

Sepertinya NAS adalah hal yang berbeda — terlepas dukungan RAID yang ditawarkan oleh NAS. NAS tidak bisa digunakan untuk menyimpan berkas sistem. Meski ada yang menawarkan Network Boot dari NAS, itu tidak memenuhi kebutuhan rumahan seperti saya. Latency network boot terlalu tinggi. Lagipula, NAS memindahkan risiko fisik dari komputer saya ke luar — yang mana saya juga tidak butuh. Terlalu banyak fitur-fiturnya yang mubazir.

Masuk dengan MFA pada antarmuka AWS CLI

| Comments

MFA (multi-factor authentication) menawarkan mekanisme login yang lebih aman. Pada AWS, kita bisa memaksa setiap pengguna agar menghidupkan MFA. Setiap kali kita login, kita akan ditanya token untuk MFA. MFA yang cukup lazim adalah menggunakan TOTP seperti Google Authenticator atau Authy.

Pada kasus kali ini, saya mengasumsikan setiap akun IAM memiliki akses yang terbatas (hanya read). Jika akun pengguna tersebut mau melakukan akses tulis, mereka harus melakukan Switch Role.

Berikut adalah script yang bisa digunakan untuk switch role melalui MFA

# Ganti ini ke ARN perangkat Anda
export DEVICE_ARN="arn:aws:iam::054121719833:mfa/mufid"

read -p "Enter token from MFA(from $DEVICE_ARN): " TOKEN
RESULT=$(aws sts get-session-token --serial-number $DEVICE_ARN --token-code $TOKEN)
export AWS_SECRET_ACCESS_KEY=$(echo "$RESULT" | yq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo "$RESULT" | yq -r '.Credentials.SessionToken')
export AWS_ACCESS_KEY_ID=$(echo "$RESULT" | yq -r '.Credentials.AccessKeyId')

echo "SecretAccessKey = $AWS_SECRET_ACCESS_KEY"
echo "SessionToken = $AWS_SESSION_TOKEN"
echo "AccessKeyId = $AWS_ACCESS_KEY_ID"

aws configure set profile.mfa.aws_secret_access_key "$AWS_SECRET_ACCESS_KEY"
aws configure set profile.mfa.aws_access_key_id "$AWS_ACCESS_KEY_ID"
aws configure set profile.mfa.aws_session_token "$AWS_SESSION_TOKEN"

echo "Selesai! Lakukan perintah ini untuk mengakses role baru"
echo
echo "         export AWS_PROFILE=secureadmin"
echo

Melakukan Analisis Log Nginx dengan GoAccess

| Comments

Dokumen ini valid saat waktu penulisan. Saya menggunakan Ubuntu 18.04 dengan GoAccess versi 1.3.

Lakukan instalasi GoAccess: [sumber]

echo "deb http://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee -a /etc/apt/sources.list.d/goaccess.list
wget -O - https://deb.goaccess.io/gnugpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install goaccess

Buka hasilnya [sumber]

cd /var/log/nginx
zcat -f access.log* | goaccess --log-format=COMBINED

Voila, selesai!

Rails Static Router

| Comments

One year ago, I need to serve single page on asset folder in my Rails application. However, I can’t use Nginx to directly serve the file since preprocessing is required by Rack middleware. Also, all of the assets is served via CDN and I can’t simply redirect the path into CDN, as JEB path my change dependencing on CDN configuration. Then I found following StackOverflow question:

Mendapatkan Surel (Email) Phising

| Comments

Ini pertama kalinya saya mendapatkan surel Phising. Surel Phising ini sendiri adalah sebuah surel yang mirip seolah-olah seperti surel asli dengan tujuan mencuri data pengguna. Kalau di SMS, ini mirip seperti “SELAMAT! NOMOR ANDA MENDAPAT HADIAH.” Padahal hadiahnya tidak ada.

Phising 1 Phising 2 Phising 3

Thermofidz: Membaca Temperatur dengan Raspberry PI

| Comments

Latar Belakang

Pada kesempatan kali ini saya iseng luar biasa ingin membuat termometer yang tersambung ke cloud. Istilah kerennya, “Internet of Things.” Ide ini muncul karena akhir-akhir ini suhu udara tidak menentu. Terkadang saya gerah sekali ketika malam hari. Saya memiliki termometer “offline” yang selalu memberi tahu saya saat ini suhu udaranya berapa. Saya memiliki hipotesis, saya akan sangat gerah dan berkeringat luar biasa jika suhu udara di atas 32 derajat celcius dan saya akan merasa sejuk jika suhu udara di bawah 28 derajat celcius.

Untuk memvalidasi hipotesis saya, tentu saja saya perlu memiliki catatan berapa suhu saat ini pada setiap waktu. Sayang sekali, termometer offline seharga Rp30.000an ini tidak memiliki catatan. Andai ada sebuah termometer yang memiliki catatan dan disimpan ke berkas, itu akan sangat membantu. Akhirnya tercetuslah ide membuat termometer sendiri.

Peralatan

Saya mengikuti tutorial dari Adafruit mengenai cara membaca temperatur dengan Raspberry. Di tutorial tersebut, mereka menggunakan sensor DS18B20. Saya segera mencari sensor tersebut di Bukalapak. di sana kita akan menemukan banyak produk, tetapi banyak pelapak yang tidak aktif juga. Saya kemarin membeli di lapak yang ini. Dan iya, harga sensornya Rp30.000an — sudah sama mahalnya dengan termometer offline yang saya beli.

Kemudian saya membeli breadboard di toko elektronik dekat kantor. Harganya Rp27.000. Saya juga membeli satu paket resistor Sparksfun di Famosa Studio. Dengan peralatan yang sudah siap di tangan, saya bisa mengikuti tutorial dari Adafruit. Skematik yang diberikan cukup straightforward. Tada, ini dia hasilnya.

!!!!!!!!!!!!!!!!!! photos goes here

Konfigurasi Raspberry

Selanjutnya adalah menyiapkan Raspberry. Saat itu saya mencoba dengan Raspberry generasi awal tipe B dengan memori 256 MB. Kemudian agar tidak lambat updatenya, saya memasang Raspbian versi terbaru ke SD Card dengan Win32DiskImager. Begitu saya tancap kabel power, Raspberry tidak tersambung ke jaringan. Bahkan dia tidak hidup, hanya ada 1 LED merah yang menyarah (power) dabntidak ada tanda-tanda kehidupan. Ups.

Penasaran, saya pindahkan SD Card ke Raspberry PI saya yang satu lagi yang berukuran 512 MB. Hidup! Ada aktivitas disk! Ada aktivitas jaringan, tetapi gagal saya ssh. Ups. Saya coba login manual dengan menancapkan kabel HDMI ke monitor dan menancapkan kabel keyboard USB. Usut punya usut, ternyata SSH daemon harus diaktifkan secara manual. Oh. Oke, perkara sederhana.

Meski demikian, saya masih penasaran mengapa versi terbaru tidak jalan di model 256 MB. Oleh karenanya saya coba memasang versi yang berbeda di SD Card dan mencoba menancapkannya di masing-masing Raspberry yang saya miliki. Berikut hasilnya:

Model/Konfigurasi 2016-03-18-raspbian-jessie-lite 2017-01-11-raspbian-jessie-lite
Raspbery PI B (Gen. 1) 256 MB Jalan Tidak mau hidup, layar boot tidak muncul
Raspbery PI B (Gen. 1) 512 MB Jalan Jalan

Hmmmm. Cukup. Aneh. Entah saya yang terburu-buru, atau memang ada galat di versi terbaru. Saya tidak yakin yang terakhir. Tapi saya sudah melakukan rewrite berulang-ulang ke SD Card saya dan hasilnya sama saja. Oke, kita settle-kan saja dengan yang 512 MB dulu.

Tutorial dari Adafruit cukup straightforward. Akan tetapi script Python nya terlalu panjang untuk saya. Saya akhirnya membuat Bash Script sederhana seperti ini:

$ cat worker.sh
#!/bin/bash

TARGET=$(find /sys/devices/w1_bus_master1/28* | grep w1_slave)
TEMP=$(cat $TARGET | grep -o 't=.*')
echo "Temperature reading is: $TEMP"

Mari kita lihat hasilnya

$ bash worker.sh
Temperature reading is: t=29250

Woow berjalan dengan sempurna! Baiklah, seharusnya, saya tinggal membuat Cronjob kemudian menyimpan hasilnya ke berkas file. Jadilah sebuah pencatat temperatur. Kendati demikian, saya ingin membuat hal yang lebih menarik yaitu menyambungkan perangkat ini ke Cloud. Oleh karena saya malas membuat basis data yang kompleks, saya memilih untuk menggunakan Git saja.

Sementara, hasil bacaan dari temperatur dapat dilihat pada akun Github saya:

mufid/thermofidz

Btw, ini hasil rangkaiannya. Tidak jauh berbeda dengan yang ditunjukkan oleh Adafruit:

Hasil Utak Atik

Yak, begitulah.

Benchmarking ActiveRecord Load: #find_in_batches (or #find_each) and #each For Processing Bulk Records

| Comments

Currently i am building a small enough Rails application, but somehow on some endpoints, we process and iterate 10k records on a single request. Sure i am caching all of those requests, but 870ms response time for first request is still unacceptable for me.

Some says it is problem with the JSON rendering. Then i installed oj gem, but the request didn’t get a noticable faster rendering. And then i deleted all JSON rendering, the request still on 800-ish ms response time. All of these findings suggest me that the problem is in the model / query itself.

When processing much records, it would be very easy to get out of memory problem if it is being done incorrectly. By default, when using #each method, Rails will load all records with specified filters (where, scope, etc) into memory. Thus, Rails’ recommendation is to use find_each command, which internally using find_in_batches method. find_in_batches will not try to load all matched records in memory. Instead, it will load records by batch.

The thing is, i was using find_each, but somehow, i was stuck on 2 seconds request. I think the problem was the find_in_batches so i changed the implementation to use find_in_batches. However, this way didn’t help me. I still got 800-ish ms response time.

Somehow, i was curious, was the problem is inside the find_in_batches? Or is it in the Ruby yield and loop? I am not sure since computer nowadays have gigahertz of computing power. However, i did the benchmark and the benchmark looks like this:

class Metadata < ApplicationRecord
  def build_summary
    points = Metadata.where('retrieved_at > \'2000-01-01 00:00:00\'')

    logger.info 'Benchmark: #find_in_batches'
    logger.info begin
      Benchmark.measure do
        points.find_in_batches do |dp|
          logger.info "End finding batches. Found: #{dp.count}"
        end
      end
    end

    logger.info 'Benchmark: #all'
    logger.info begin
      Benchmark.measure do
        points.each do |dp|
          # yield nothing
        end
      end
    end

    logger.info 'Benchmark: 10k loop'
    logger.info begin
      Benchmark.measure do
        (1..10_000).each do |i|
          # yield no one
        end
      end
    end
  end
end

I added ‘normal’ loop, just curious if the problem is inside the Ruby’s interpreter.

Here is the result. I did this on Dual Core 4 GB RAM Virtualbox inside Intel Core i5 with 16 GB RAM. I used Ruby 2.3.1 and Rails 5.0.0.1. For database, i use PostgreSQL 9.6.

I, [2016-12-15T14:56:37.463516 #28376]  INFO -- : Benchmark: #find_in_batches
I, [2016-12-15T14:56:37.511837 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.516353 #28376] DEBUG -- :   Metadata Load (3.2ms)
I, [2016-12-15T14:56:37.550252 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.553774 #28376] DEBUG -- :   Metadata Load (2.8ms)
I, [2016-12-15T14:56:37.596984 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.603011 #28376] DEBUG -- :   Metadata Load (5.4ms)
I, [2016-12-15T14:56:37.645731 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.652892 #28376] DEBUG -- :   Metadata Load (6.5ms)
I, [2016-12-15T14:56:37.692978 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.696923 #28376] DEBUG -- :   Metadata Load (3.1ms)
I, [2016-12-15T14:56:37.738265 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.742807 #28376] DEBUG -- :   Metadata Load (3.5ms)
I, [2016-12-15T14:56:37.787560 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.792086 #28376] DEBUG -- :   Metadata Load (3.1ms)
I, [2016-12-15T14:56:37.828058 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.836220 #28376] DEBUG -- :   Metadata Load (7.2ms)
I, [2016-12-15T14:56:37.907940 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.911302 #28376] DEBUG -- :   Metadata Load (2.8ms)
I, [2016-12-15T14:56:37.943847 #28376]  INFO -- : End finding batches. Found: 1000
D, [2016-12-15T14:56:37.945335 #28376] DEBUG -- :   Metadata Load (0.9ms)
I, [2016-12-15T14:56:37.949258 #28376]  INFO -- : End finding batches. Found: 157
I, [2016-12-15T14:56:37.949421 #28376]  INFO -- :   0.330000   0.010000   0.340000 (  0.485759)
I, [2016-12-15T14:56:37.949613 #28376]  INFO -- : Benchmark: #all
D, [2016-12-15T14:56:37.991020 #28376] DEBUG -- :   Metadata Load (40.9ms)
I, [2016-12-15T14:56:38.218292 #28376]  INFO -- :   0.220000   0.000000   0.220000 (  0.268597)
I, [2016-12-15T14:56:38.218840 #28376]  INFO -- : Benchmark: 10k loop
I, [2016-12-15T14:56:38.219272 #28376]  INFO -- :   0.000000   0.000000   0.000000 (  0.000362)

Sure it is not the Ruby’s interpreter problem since vanilla 10k loop only took 0.3 msec. Comparable to native speed, huh? But we found several interesting findings:

  • using #each is faster than using #find_in_batches (480 ms to 260 ms)
  • … What “{Model} Load (some ms)” means? It does not even reflect the real load time. “Metadata Load 40.9 ms”, meanwhile the real load time is 268 ms. If we compare from logger timestamp, it is also around 268 ms (38.218 – 37.949). Seems like internally they use different measurement tip.
  • For each group in find_in_batches, the “Metadata Load” itself does not reflect the real time to yield the method. If we compare each DEBUG line, it will be around 40ms (e.g.: Look 37.550252 – 37.516353), pretty far from 3.2ms.

So what is my solution for processing 10k records? I don’t know. The page itself will rarely change, so caching the request will help it much. But i am still curious, why processing only 10k records can took up to 800ms.

Also, i am still curious why “Model Load” says faster than the time it is actually need. It took 40ms between yield but it says loaded in 3.2ms.