Tuesday, August 16, 2016

Redis : Clustering dan Failover

Redis adalah in-memory data structure store. Redis sangatlah cepat sebenarnya. Biasa digunakan sebagai caching sistem, database atau message queue. Format data redis adalah key-value.
Format data yang didukung antara lain strings, hashes, lists, sets, sorted sets. Perusahaan sekelas twitter pun menggunakan Redis untuk caching system. Yang dapat mengungurangi load ke database atau storage utama lainnya.
Tapi kita tidak akan tentang feature atau penggunaan redis. Kita akan membahas tentang redis clustering dan failover (satu host mati, yang lain akan membackup).
Bagaimana mencluster redis menjadi banyak instance dan mengatasi jika redis master mengalami kegagalan dengan waktu downtime sekecil mungkin (sebisa mungkin zero second downtime).
Istilahnya mungkin High Availability.

Requirement untuk tutorial ini :
- redis 3.0xx (include sentinel)
- HAProxy 1.5xx

Kebanyakan dari kita mungkin awalnya memakai redis dengan cara seperti ini.



Satu atau lebih application server konek ke single redis (satu redis master). sebenarnya tidak masalah karena akses redis sangatlah cepat.
Tapi setelah beberapa saat, setelah jumlah app server yang connect bertambah dan jumlah request dari client naik jug. Maka jumlah koneksi ke redis akan bertambah juga secara signifikan.
Jumlah key bertambah, memory consume naik. Kemungkinan pada suatu saat akan terjadi kegagalan di redis, misal out memory atau disk corrupt. App server juga ikut gagal karen tidak bisa membaca data dari redis.
Maka terjadilah downtime. Downtime disini bukan berarti app server mati, tetapi hanya client tidak bisa mengakses datanya karena kegagalan di redis.
Sehingga perlu dilakukan backup. Bisa menaikan memory di redis instance. Atau bisa jadi malah ganti VM untuk redis. Kemudian copy data yg lama. Proses ini tentu membutuhkan waktu beberapa menit bahkan beberapa jam sampai app server bisa mendapatkan datanya kembali.

Kemudian kita coba cara lain seperti dibawah ini.



Konfigurasi redis.conf 
Start redis : ./redis-server PATH/redis.conf
 #redis-01 
 port 6380  
 bind 192.168.33.10 
 #redis-02  
 bind 6381  
 bind 192.168.33.20  
 slaveof 192.168.33.10 6380  
App server tetep connect ke satu redis master. Tetapi di belakang redis master ada satu atau bisa banyak redis slave.
Ini bisa disebut satu redis custer.
Redis cluser bisa diartikan kumpulan redis yang tugasnya sama biasanya berisi : 1 Master + N x Slave)
Redis Slave tugasnya melakukan syncronization data ke redis master. Redis sifatnya read-only. Lalu apa keuntungannya?
Ketika Redis master mengalami kegagalan (dengan berbagai alasan). Maka kita cukup mempromote salah satu Redis slave
untuk menjadi redis master. App server diganti pointing ke redis master yg baru, dan redis slave uang lain disetting sebagai anggota (slave) dari Redis master yang baru.
Kita tinggal berkonsentrasi untuk perbaikan redis master yang bermasalah lagi. Ketika sudah up bisa di kembalikan ke cluster sebagi master atau slave.
Downtime yang terjadi bisa jauh lebih berkurang karena kita tinggal menganti konfigurasi host redis di app server dan setting master-slave satu-persatu di sisi redis cluster. Kemudian restart semua service.

Apa yang harus kita lakukan untuk mengurangi downtime lagi?
Sekarang kita coba memasang Redis Sentinel. Apa itu Sentinel? (dari redis.io)
"Redis Sentinel provides high availability for Redis. In practical terms this means that using Sentinel you can create a Redis deployment that resists without human intervention to certain kind of failures.
Redis Sentinel also provides other collateral tasks such as monitoring, notifications and acts as a configuration provider for clients.
This is the full list of Sentinel capabilities at a macroscopical level (i.e. the big picture):
Sentinel constantly checks if your master and slave instances are working as expected.
Sentinel can notify the system administrator, another computer programs, via an API, that something is wrong with one of the monitored Redis instances.
Automatic failover. If a master is not working as expected, Sentinel can start a failover process where a slave is promoted to master, the other additional slaves are reconfigured to use the new master, and the applications using the Redis server informed about the new address to use when connecting.
Sentinel acts as a source of authority for clients service discovery: clients connect to Sentinels in order to ask for the address of the current Redis master responsible for a given service. If a failover occurs, Sentinels will report the new address."
Intinya sentinel itu bisa melakukan monitoring ke sebuah redis cluster. Bisa mendekteksi pada saat master mengalami kegagalan. Maka sentinel otomatis akan mempromote salah satu savle menjadi master, dan mereset semua konfigurasi slave untuk pointing ke master yang baru.
dan jika master yang lama sudah hidup kembali. Sentinel akan otomatis mensetting menjdai slave. Begitu seterusnya posesnya jika ada master yang gagal.
Downtime akan jauh lebih berkurang karena konfigurasi master-slave yang baru dihandle oleh Sentinel. Kita cukup mengganti konfigurasi di app server mau connect ke host redis yang mana.

Konfigurasi : sama dengan master-slave sebelumnya kita tambahkan satu mesin lagi yang berfungsi sebagai sentinel (sentinel.conf)

 #sentinel conf  
 port 16380  
 bind 192.168.33.10  
 sentinel monitor redis-cluster 192.168.33.10 6380 1  
 sentinel down-after-milliseconds redis-cluster 5000  
 sentinel failover-timeout redis-cluster 10000  
 sentinel config-epoch redis-cluster 26  
Start sentinel : ./redis-server PATH/sentinel.conf --sentinel
Sentinel akan otomatis mendeteksi redis mana aja yang menjadi anggota dari cluster redis-01 (192.168.33.10)
 #sentinel log  
 1793:X 12 Aug 05:16:55.269 # Sentinel runid is b2e912eedf1416b61ecb8688180c64f00f0e12b6  
 1793:X 12 Aug 05:16:55.269 # +monitor master redis-cluster 192.168.33.10 6380 quorum 1  
 1793:X 12 Aug 05:16:56.269 * +slave slave 192.168.33.20:6381 192.168.33.20 6381 @ redis-cluster 192.168.33.10 6380  

Kemudian kita stop redis master pertama (3310:6380), seolah-olah sedang down.
 # sentinel failover  
 1793:X 12 Aug 05:17:37.074 # +sdown master redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.075 # +odown master redis-cluster 192.168.33.10 6380 #quorum 1/1  
 1793:X 12 Aug 05:17:37.076 # +new-epoch 27  
 1793:X 12 Aug 05:17:37.076 # +try-failover master redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.077 # +vote-for-leader b2e912eedf1416b61ecb8688180c64f00f0e12b6 27  
 1793:X 12 Aug 05:17:37.078 # +elected-leader master redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.078 # +failover-state-select-slave master redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.152 # +selected-slave slave 192.168.33.20:6381 192.168.33.20 6381 @ redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.152 * +failover-state-send-slaveof-noone slave 192.168.33.20:6381 192.168.33.20 6381 @ redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.244 * +failover-state-wait-promotion slave 192.168.33.20:6381 192.168.33.20 6381 @ redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.538 # +promoted-slave slave 192.168.33.20:6381 192.168.33.20 6381 @ redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.538 # +failover-state-reconf-slaves master redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.595 # +failover-end master redis-cluster 192.168.33.10 6380  
 1793:X 12 Aug 05:17:37.595 # +switch-master redis-cluster 192.168.33.10 6380 192.168.33.20 6381  
 1793:X 12 Aug 05:17:37.596 * +slave slave 192.168.33.10:6380 192.168.33.10 6380 @ redis-cluster 192.168.33.20 6381  
 1793:X 12 Aug 05:17:42.656 # +sdown slave 192.168.33.10:6380 192.168.33.10 6380 @ redis-cluster 192.168.33.20 6381  

Otomatis redis kedua (33.20:6381) akan menjadi master.
 andris-MacBook-Pro:~ andri$ redis-cli -h 192.168.33.20 -p 6381  
 192.168.33.20:6381> role  
 1) "master"  
 2) (integer) 0  

Ketika redis pertama (33.10:6381) tadi up kembali, maka ia akan otomatis menjadi slave
 #redis-01 is up and will be slave of redis-01  
 1793:X 12 Aug 05:20:10.131 # -sdown slave 192.168.33.10:6380 192.168.33.10 6380 @ redis-cluster 192.168.33.20 6381  
 1793:X 12 Aug 05:20:20.101 * +convert-to-slave slave 192.168.33.10:6380 192.168.33.10 6380 @ redis-cluster 192.168.33.20 6381  
Cek redis role yang baru, yang sudah jadi slave.
 andris-MacBook-Pro:~ andri$ redis-cli -h 192.168.33.10 -p 6380  
 192.168.33.10:6380> role  
 1) "slave"  
 2) "192.168.33.20"  
 3) (integer) 6381  
 4) "connected"  

Masalah dari konfigurasi diatas adalah app server tidak langsung tahu mana redis master yang baru, setelah redis master yang lama masih mati.
Kita bisa memakai HAProxy untuk mengatasi ini. HAproxy biasanya digunakan untuk load balance http request. Install haproxy bisa lewat apt-get jika memakai linux ubuntu. Pada versi 1.5x keatas sudah support untuk koneksi TCP (redis memakai TCP). HAProxy akan selalu melakukan healty-check ke semua redis yang sudah didaftarkan mencari mana yang master. Ketika Sentinel sudah merubah master-slave yang baru, HAProxy akan langsung tahu mana yang master. App server atau client connect TCP ke HAProxy host bukan ke redis langsung (tanpa tahu proses master-slave dibelakang). Proses downtime di app server akan jauh lebih turun lagi.
 #HAProxy conf  
 defaults REDIS  
      log   global  
      mode tcp  
      timeout connect 4s  
      timeout server 30s  
      timeout client 30s  
 frontend ft_redis  
      bind *:6378 name redis  
      default_backend backend_redis  
 backend backend_redis  
      option tcp-check  
      tcp-check connect  
      tcp-check send PING\r\n  
      tcp-check expect string +PONG  
      tcp-check send info\ replication\r\n  
      tcp-check expect string role:master  
      tcp-check send QUIT\r\n  
      tcp-check expect string +OK  
      server redis_01 192.168.33.10:6380 check inter 1s  
      server redis_02 192.168.33.20:6381 check inter 1s  

Konfigurasi redis sentinel sama dengan sebelumnya.

Percobaan yang dilakukan ke HAProxy (33.80:6379)
 andris-MacBook-Pro:~ andri$ redis-cli -h 192.168.33.80 -p 6378  
 192.168.33.80:6378> ping  
 PONG  
 192.168.33.80:6378> role  
 1) "master"  
 2) (integer) 169  
 3) 1) 1) "192.168.33.20"  
    2) "6381"  
    3) "169"  
 192.168.33.80:6378> set foo "bar"  
 OK  
 192.168.33.80:6378> get foo  
 "bar"  
 192.168.33.80:6378> get foo  
 "bar"  

Kita matikan redis-master pertama (33.10:6380).
 #sentinel log  
 2721:X 12 Aug 09:19:58.218 # +failover-state-reconf-slaves master redis-cluster 192.168.33.10 6380  
 2721:X 12 Aug 09:19:58.316 # +failover-end master redis-cluster 192.168.33.10 6380  
 2721:X 12 Aug 09:19:58.317 # +switch-master redis-cluster 192.168.33.10 6380 192.168.33.20 6381  
 2721:X 12 Aug 09:19:58.318 * +slave slave 192.168.33.10:6380 192.168.33.10 6380 @ redis-cluster 192.168.33.20 6381  
 2721:X 12 Aug 09:20:03.375 # +sdown slave 192.168.33.10:6380 192.168.33.10 6380 @ redis-cluster 192.168.33.20 6381  

Data masih tetap ada, tanpa kita ganti konfigurasi redis di app server.
 192.168.33.80:6378> get foo  
 "bar"  

Sekian tutorial ini, semoga bisa bermanfaat. :)