Tự động backup database với gem backup và whenever trong Rails
Bạn sẽ cần bản sao lưu của cơ sở dữ liệu để khôi phục toàn bộ cơ sở dữ liệu đó nếu có lỗi hệ thống hoặc để khôi phục một đối tượng khi lệnh Hoàn tác không đủ để sửa lỗi. Nếu bạn thấy bản sao lưu của cơ sở dữ liệu dường như là sự lãng phí dung lượng ...
Bạn sẽ cần bản sao lưu của cơ sở dữ liệu để khôi phục toàn bộ cơ sở dữ liệu đó nếu có lỗi hệ thống hoặc để khôi phục một đối tượng khi lệnh Hoàn tác không đủ để sửa lỗi.
Nếu bạn thấy bản sao lưu của cơ sở dữ liệu dường như là sự lãng phí dung lượng lưu trữ, hãy nghĩ tới thời gian mà bạn sẽ tiết kiệm được nhờ tránh bị mất dữ liệu và thiết kế. Tạo bản sao lưu theo định kỳ là điều đặc biệt quan trọng khi bạn có một vài người dùng cùng cập nhập một cơ sở dữ liệu. Không có bản sao lưu, bạn sẽ không khôi phục được các đối tượng bị thiếu hoặc bị hỏng hoặc bất kỳ thay đổi nào đối với thiết kế cơ sở dữ liệu.
Trong bài viết dưới đây tôi sẽ giới thiếu cho các bạn các sao lưu sữ liệu bằng cách sử dung gem backup và whenever trong Ruby on Rails.
Phần này các bạn cần chú ý không thêm gem "backup" vào trong file Gemfile trong ứng dụng của các bạn. Thay vào đó các bạn nên cài đặt trực tiếp thông qua lệnh sau:
gem install backup
Sau đó bạn có thể chạy sao lưu làm quen với tất cả các lệnh mà gem cung cấp. Chúng ta sẽ bắt đầu bằng cách tạo ra một model backup, và chỉ đơn giản là một mô tả về cách backup sẽ làm việc như thế nào. Nếu bạn chạy lệnh sau:
backup help generate:model
bạn sẽ thấy tất cả các tùy chọn có sẵn để mô tả cách chúng ta tạo một model để backup. Dưới đây là lệnh và các option ví dụ mà tôi dùng để tạo một model backup:
backup generate:model --trigger=db_backup --databases='postgresql' --storages='scp' --compressor='gzip' --notifiers='mail'
Bạn có thể thấy, option đầu tiên là --trigger để tạo một model tên là db_backup. Tiếp theo đó là --databases là option để xác định tôi sẽ sử dụng cơ sở dữ liệu PostgreSQL. (Ngoài ra gem backup còn hỗ trợ các cơ sở dữ liệu khác như: MySQL, MongoDB, Redis, and Riak.)
Tiếp theo, tôi sử dụng --storages nói sao lưu như thế nào để thực hiện việc sao lưu chính nó. Bằng cách xác định scp, tôi nói rằng các tập tin sao lưu sẽ được lưu trữ trên một VPS khác, và nó nên được chuyển giao qua SCP. (Lý tưởng nhất, VPS phụ của bạn phải ở trong một vị trí đó là khác so với các VPS chạy cơ sở dữ liệu của bạn.) Ngoài SCP, Backup cũng hộ trợ rsync, FTP/SFTP, S3, Dropbox, and một vài thứ khác.
Sau đó tôi xác định rằng tôi muốn backup của tôi được nén dạng zip (bạn cũng có thể sử dụng bzip2, nếu bạn muốn), và cuối cùng, tôi nói với Backup để thông báo cho tôi qua email khi sao lưu thành công hay thất bại. Nếu bạn không thích email, các tùy chọn khác của bạn bao gồm Twitter, Prowl, Campfire, Hipchat, và những thứ khác.
Cuối cùng sau khi các bạn chạy lệnh trên, các bạn sẽ sinh ra cácfile như sau:
Generated configuration file: '/home/trungnt/Backup/config.rb'. # trong trường hợp lần đầu tiên chạy lệnh generate Generated model file: '/home/trungnt/Backup/models/db_backup.rb'. # file model chúng ta cần tạo
# db_backup.rb # encoding: utf-8 ## # Backup Generated: db_backup # Once configured, you can run the backup with the following command: # # $ backup perform -t db_backup [-c <path_to_configuration_file>] # # For more information about Backup's components, see the documentation at: # http://backup.github.io/backup # Model.new(:db_backup, 'Description for db_backup') do ## # PostgreSQL [Database] # database PostgreSQL do |db| # To dump all databases, set `db.name = :all` (or leave blank) db.name = "my_database_name" db.username = "my_username" db.password = "my_password" db.host = "localhost" db.port = 5432 db.socket = "/tmp/pg.sock" # When dumping all databases, `skip_tables` and `only_tables` are ignored. db.skip_tables = ["skip", "these", "tables"] db.only_tables = ["only", "these", "tables"] db.additional_options = ["-xc", "-E=utf8"] end ## # SCP (Secure Copy) [Storage] # store_with SCP do |server| server.username = "my_username" server.password = "my_password" server.ip = "123.45.678.90" server.port = 22 server.path = "~/backups/" server.keep = 5 # server.keep = Time.now - 2592000 # Remove all backups older than 1 month. # Additional options for the SSH connection. # server.ssh_options = {} end ## # Gzip [Compressor] # compress_with Gzip ## # Mail [Notifier] # # The default delivery method for Mail Notifiers is 'SMTP'. # See the documentation for other delivery options. # notify_by Mail do |mail| mail.on_success = true mail.on_warning = true mail.on_failure = true mail.from = "sender@email.com" mail.to = "receiver@email.com" mail.cc = "cc@email.com" mail.bcc = "bcc@email.com" mail.reply_to = "reply_to@email.com" mail.address = "smtp.gmail.com" mail.port = 587 mail.domain = "your.host.name" mail.user_name = "sender@email.com" mail.password = "my_password" mail.authentication = "plain" mail.encryption = :starttls end end
Tôi khuyên bạn nên thiết lập cài đặt thông tin bằng các lưu vào một tệp tin chuyên dụng hoặc đặt vào các biến môi trường, thay vì chóa chúng trong mỗi lần sử dụng. Chúng ta có thể sưa lại như sau:
# encoding: utf-8 # load login info db_config = YAML.load_file('/var/www/phindee/shared/config/database.yml')['production'] app_config = YAML.load_file('/var/www/phindee/shared/config/application.yml') Model.new(:db_backup, 'backs up ip_addresses table') do # PostgreSQL [Database] database PostgreSQL do |db| db.name = db_config['database'] db.username = db_config['username'] db.password = db_config['password'] db.host = "localhost" db.only_tables = ["ip_addresses"] end # SCP (Secure Copy) [Storage] store_with SCP do |server| server.username = app_config['backup_username'] server.password = app_config['backup_password'] server.ip = app_config['backup_ip'] server.port = app_config['backup_port'] server.path = "~/backups/" server.keep = 5 end # Gzip [Compressor] compress_with Gzip # Mail [Notifier] notify_by Mail do |mail| mail.on_success = false mail.on_warning = true mail.on_failure = true mail.from = app_config['email_username'] mail.to = app_config['email_username'] mail.address = app_config['email_address'] mail.port = app_config['email_port'] mail.domain = app_config['email_domain'] mail.user_name = app_config['email_username'] mail.password = app_config['email_password'] mail.authentication = :login mail.encryption = :ssl end end
Tôi bổ sung thêm hai dòng trong đầu để lấy ra các thông tin đăng nhập cần thiết từ những tập tin bằng cách sử dụng phương pháp load_file () từ các module YAML. Ở đây tôi sử dụng là lưu thông tin về cơ sở dữ liêu vào file database.yml, bên cạnh đó email and VPS information in application.yml.
Các bạn hãy nhìn vào db_backup model, nó bao gồm 4 sections. Bởi vì chúng ta xác định PostgreSQL cho các tùy chọn --databases, phần đầu tiên đó là cấu hình đó cho PostgreSQL. Chúng ta sẽ khai báo với backup thông tin về database, username, password và host cùng với mảng các bảng muốn sao lưu. Mảng này là tùy chọn và chỉ nên dùng nếu bạn không muốn toàn bộ cơ sở dữ liệu của bạn. (Tôi sử dụng nó vì bảng ip_addresses là bảng duy nhất tôi quan tâm đến việc sao lưu từ các dữ liệu cho tất cả các bảng khác của tôi được lưu trong seed.rb.)
Phần thứ hai mô tả làm thế nào để kết nối với VPS thứ hai. Sau khi thiết username, password, P address và port, tôi chỉ định đường dẫn mà các bản sao lưu sẽ được lưu trữ, và tôi giữ chỉ giữ lại năm bản backup gần đây nhất. Phần thứ ba chỉ cần nói với Backup để sử dụng gzip để nén, trong khi cuối cùng chứa các tùy chọn cho việc thiết lập các thông báo email, mà nói cho Backup để chỉ gửi một email nếu có cảnh báo hoặc một thất bại xảy ra.
Sau khi đã cấu hình xong cho backup trong file db_backup.rb, chúng ta có thể chạy nó với lệnh sau đây:
backup perform -t db_backup
Nếu thành công, bạn sẽ có thể tìm thấy một tập tin sao lưu gzip trên VPS phụ của bạn và nhận một email gửi về trong email mà bạn cung cấp.
Với những cài đặt bên trên, đến đây là tất cả tuyệt vời, nhưng nó sẽ không được hoàn hảo nếu sao lưu được thực hiện tự động mà không cần phải kích hoạt nó? Điều này hoàn toàn có thể với một công cụ là cron. Nếu bạn không quen thuộc với nó, cron là một tiện ích lập lịch trình cho phép bạn chạy các nhiệm vụ (được biết đến như là công việc cron) vào những thời điểm quy định. Bạn có thể sử dụng nó để tự động hóa công việc mà cần phải được chạy đều đặn. Nếu bạn chưa bao giờ sử dụng nó trước, DigitalOcean có một bài viết tốt giới thiệu đáng để đọc.
Để viết cron jobs, chúng ta sẽ sử dụng gem Whenever, bởi vì nó cho phép chúng ta viết chúng trong một đơn giản, nó viết theo Ruby syntax thay cho syntax chuẩn của cron.
Chúng ta sẽ cài đặt gem bằng lệnh:
gem install whenever
Sau đó , tạo ra một thư mục /config cho Whenever bên trong ~/Backup:
cd ~/Backup mkdir config
Rồi chạy lệnh:
wheneverize .
Lệnh này sẽ tạo ra một file schedule.rb trong ~/Backup/config để viết các cronjob. Dưới đây là đoạn code tôi thêm vào:
# ~/Backup/config/schedule.rb every 1.day, :at => '11:30 pm' do # chạy hàng ngày vào lúc 11h30 tối command "backup perform -t db_backup" end
Đoạn code trên sẽ thực hiện lệnh "backup perform -t db_backup" vào lúc 23h30 hàng ngày. Nếu bạn muốn nhìn thấy điều này chuyển đổi vào syntax của cron, chạy whenever:
$ whenever 30 23 * * * /bin/bash -l -c 'backup perform -t db_backup >> /home/bob/Backup/config/cron.log 2>&1'
Điều này được gọi là crontab của bạn (viết tắt của cron table), và nó sẽ liệt kê tất cả các công việc cron được lên kế hoạch để chạy, cùng với thời gian và ngày chúng sẽ chạy.
Bởi vì chạy whenever không thực sự viết công việc của chúng tôi vào crontab, chúng tôi sẽ cần phải chạy:
whenever --update-crontab
làm như vậy. Khi làm điều đó, bây giờ cron sẽ biết về công việc của chúng ta, và nó sẽ được thực hiện tại thời gian quy định hàng ngày. Khi nó chạy, nó cũng sẽ ghi lại hoạt động của nó trong ~/Backup/config/cron.log file để refences trong tương lai.
Để làm cho nó dễ dàng hơn để chỉnh sửa các file trong tương lai, tôi quyết định để sử dụng lại chúng trên local và lưu trữ chúng trong thưc mục /config gọi là /backup, có nghĩa là bây giờ nó sẽ được kiểm soát phiên bản là tốt. Và vì tôi sử dụng Capistrano cho việc deploy, tôi đã viết hai task để tự động hóa quá trình tải lên các tập tin về server. Chúng được đặt trong một file là backup.cap trong folder /lib/capistrano/tasks:
# /lib/capistrano/tasks/backup.cap namespace :backup do desc "Upload backup config files." task :upload_config do on roles(:app) do execute "mkdir -p #{fetch(:backup_path)}/models" upload! StringIO.new(File.read("config/backup/config.rb")), "#{fetch(:backup_path)}/config.rb" upload! StringIO.new(File.read("config/backup/models/db_backup.rb")), "#{fetch(:backup_path)}/models/db_backup.rb" end end desc "Upload cron schedule file." task :upload_cron do on roles(:app) do execute "mkdir -p #{fetch(:backup_path)}/config" execute "touch #{fetch(:backup_path)}/config/cron.log" upload! StringIO.new(File.read("config/backup/schedule.rb")), "#{fetch(:backup_path)}/config/schedule.rb" within "#{fetch(:backup_path)}" do # capistrano was unable to find the executable for whenever # without the path to rbenv shims set with path: "/home/#{fetch(:deploy_user)}/.rbenv/shims:$PATH" do puts capture :whenever puts capture :whenever, '--update-crontab' end end end end end
Trong file /config/deploy.rb, sau đó có định nghĩa sau đây để biến backup_path:
# deploy.rb [...] set :backup_path, "/home/#{fetch(:deploy_user)}/Backup" [...]
Và với điều đó, chức năng sao lưu của chúng tôi là hoàn tất. Bây giờ bạn sẽ có một bản sao lưu cơ sở dữ liệu của bạn được lưu trữ trên một VPS thứ cấp mỗi 24 giờ mà không cần phải nhấc một ngón tay! Và thậm chí nó sẽ thông báo cho bạn nếu nó không thành công!