Cara Migrasi Rails Storage dari Local VPS ke Cloudflare R2
Panduan singkat untuk memindahkan file ActiveStorage Rails dari storage lokal ke Cloudflare R2 secara gratis (10 GB) menggunakan rclone: pasang dan konfigurasikan remote r2, jalankan rclone sync dengan --dry-run untuk verifikasi lalu eksekusi sinkronisasi, lakukan transformasi path menjadi flat dengan rclone moveto, dan terakhir update ActiveStorage::Blob mengganti service_name dari local ke r2.
Waktu baca: ~ 3 menit
Secara default, storage service yang digunakan oleh sebuah aplikasi Rails adalah local service. Ini berarti, file-file yang di-upload via aplikasi Rails akan disimpan di folder storage di server yang sama dengan server aplikasi.
Tidak ada yang salah dengan ini, tapi jika karena satu dan lain hal, kamu harus memindahkan server aplikasi kamu ke server yang lain, maka kamu juga harus memindahkan file-file yang tersimpan di folder storage ke server baru tersebut. Belum lagi kalau misalnya kamu lupa bayar tagihan bulanan server, sehingga pihak layanan menonaktifkan dan menghapus semua data di server kamu. Selesai sudah.
Oleh karena itu, sangat disarankan untuk memisahkan server aplikasi dengan server storage.
Problemnya, menambah server storage berarti menambah biaya operasional. Bagi proyek personal yang belum terlalu atau bahkan tidak menghasilkan revenue sama sekali, tentu ini patut jadi pertimbangan.
Untungnya, Cloudflare R2 menyediakan free storage sebesar 10 GB. Sehingga, jika masih dalam skala kecil, biaya operasional untuk server storage masih 0.
Ganti Service jadi R2
Langkah pertama yang harus dilakukan adalah menambah storage service baru di config/storage.yml dengan nama r2.
# config/storage.yml
r2:
service: S3
endpoint: <%= ENV["R2_STORAGE_ENDPOINT"] %>
access_key_id: <%= ENV["R2_ACCESS_KEY_ID"] %>
secret_access_key: <%= ENV["R2_SECRET_ACCESS_KEY"] %>
region: auto
bucket: bucket-name
request_checksum_calculation: when_required
response_checksum_validation: when_required
Ganti bucket-name dengan nama bucket yang akan digunakan di Cloudflare R2. Pastikan bucket tersebut sudah ada, karena error akan terjadi jika pada saat upload file, bucket-nya belum ada.
Lalu, atur environment variable R2_STORAGE_ENDPOINT, R2_ACCESS_KEY_ID, dan R2_SECRET_ACCESS_KEY . Masuk ke halaman Cloudflare Dashboard > Storage & databases > R2 Object Storage > Overview lalu klik Manage dan pilih Create Account API token untuk mendapatkan access_key_id , secret_access_key , dan endpoint.
Buka config/environments/production.rb, lalu ganti storage service menjadi r2 .
# config/environments/producttion.rb
Rails.application.configure do
# other codes goes here
...
config.active_storage.service = :r2
# other codes goes here as well
...
end
Commit lalu deploy changes-nya.
Sync Rails Storage ke Cloudflare R2
Masalah selanjutnya adalah bagaimana cara memindahkan existing files di dalam folder storage ke Cloudflare R2? Untuk ini, kita memerlukan suatu tools bernama rclone.
SSH ke server kamu sebagai root, lalu instal rclone sesuai OS yang digunakan di server. Karena saya pakai Ubuntu Server, perintahnya adalah apt install rclone.
Setelah rclone terinstal, jalankan rclone config untuk menambahkan remote credentials baru.
- Atur nama remote-nya sebagai
r2 - Tekan 5 lalu enter untuk memilih Amazon S3 Compliant Storage Providers
- Tekan 5 lalu enter untuk memilih Cloudflare R2 Storage
- Tekan 1 lalu enter untuk emilih Enter AWS credentials in the next step.
- Input
access_key_id,secret_access_key,region, danendpoint - Kamu bisa pakai
access_key_id,secret_access_key, danendpointyang sudah kamu buat sebelum langkah ini. - Atur
regionjadiautosaja.
- Kamu bisa pakai
- Tekan n lalu enter untuk skip edit advanced config
- Tekan y lalu enter untuk menyimpan remote.
Setelah itu, saatnya jalankan sinkronisasi.
Dry Run Terlebih Dahulu
Sebelum melakukan sinkronisasi, saya sangat menyarankan untuk menjalankan dry run terlebih dahulu. Cukup tambah flag --dry-run di akhir perintah yang akan dieksekusi.
Sehingga, jalankan rclone sync /path/to/storage r2:bucket-name --progress --exclude="*.sqlite3*" --exclude=".keep" --dry-run untuk mengecek apakah object yang akan di-sync sudah sesuai atau belum, termasuk apakah file .keep dan SQLite sudah benar-benar dikecualikan. Sesuaikan perintah jika kamu ingin mengecualikan file-file lain.
Jika kamu deploy pakai Docker, ganti /path/to/storage dengan path ke docker volume, misal /var/lib/docker/volumes/app_storage/_data.
Setelah hasilnya sudah sesuai, jalankan perintah yang sama tanpa flag --dry-run. Tunggu hingga selesai.
Buat Path Object jadi Flat
Service storage local di Rails, untuk alasan tertentu, akan selalu menambahkan dua nested directory untuk setiap blob yang disimpan berdasarkan key blob tersebut. Masing-masing directory akan mengambil dua huruf pertama dan dua huruf kedua dari key blob.
Misal, jika key-nya itu abcdefghijkl, maka service local akan selalu menyimpan setiap file yang diupload dengan nama ab/cd/abcdefghijkl.
Dampaknya, setelah sync ke Cloudflare R2 berhasil, kita harus membuat path-nya jadi flat sebelum mengganti service name, sehingga formatnya menjadi bucket-name/abcddefghijkl. Kita bisa melakukannya via rclone dengan perintah berikut.
rclone lsf r2:bucket-name --recursive | while read file; do
base=$(basename "$file")
rclone moveto "r2:bucket-name/$file" "r2:bucket-name/$base" --progress
done
Pastikan untuk menjalankan dry run terlebih dahulu sebelum benar-benar mengeksekusinya, seperti saat menjalankan perintah sync.
Update Service Name di ActiveStorage::Blob
Ini adalah langkah terakhir proses migrasi. Jalankan kode berikut via migrasi atau Rails console untuk mengubah service name dari local menjadi r2.
ActiveStorage::Blob.where(service_name: "local").update_all(service_name: "r2")
Jika kamu melakukannya via migrasi, pastikan untuk jalankan rails db:migrate dan deploy changes-nya ke production.
Semoga bermanfaat!