LoginSignup
2
2

More than 5 years have passed since last update.

Gem in a Boxサーバーのデータをrsyncで同期する

Last updated at Posted at 2015-08-30

TL;DR

  • Gem in a BoxのGemを別のGem in a Boxサーバーに同期する
    • 新しくpushされたとき
    • 削除されたとき
  • Rack Middlewareでrsyncを使う

はじめに

Gem in a BoxのデータをRackのMiddlewareを使用して同期する方法を紹介します。
プライベートなGemサーバーをCIに組み込む時にGemデータを同期して冗長化する必要があったのでこの方法をとりました。

あくまでもデータ同期だけです。Active-Standbyを想定してます。

方針

やりたいこと

  • Gem in a BoxにpushしたGemをpushされた時に同期する
  • 削除された時も同期削除する

どうするか

  • RackでPOSTリクエストまたはDELTEリクエストが来た時にrsyncでデータディレクトリを同期する
  • 同期してからレスポンスを返す
  • RackのMiddlewareで実現する
  • Proxyしてるrubygems.orgのGemなんかはどうでもいい

そのために

  • rsyncするRack Middlewareを使う
  • rsyncするため Primary -> Secondary へSSH接続できるようにする
    • Gem in a Box用のユーザを作る
    • 秘密鍵・公開鍵のペアを適切に設定する

その他

  • Unicornを使う
  • systemdでUnicornを起動する
  • systemdの環境ファイルにお互いのIPを持つ
  • Rackは環境ファイルからの環境変数で自分がPrimaryかSecondaryか判断する

注意点

  • rsyncなのでGem数が増えたとき時間かかるかも
    • rubygems.orgをProxyしなければいい
  • GETに比べてPOST/DELETEが少ない前提
  • Unicornが落ちたらデータが同期できない

やってみる

Vagrantでやってみます。一式をGithubに用意しました。
https://github.com/nownabe/example-geminabox-replication

まずこのレポジトリをクローンしてください。

git clone https://github.com/nownabe/example-geminabox-replication
cd example-geminabox-replication

VagrantでGem ServerのPrimary/Secondaryとなる2台のVMを起動してプロビジョンします。
Rubyをビルドするので時間かかります。

vagrant up

このVagrantfileを使うと、次のIPアドレスで起動されます。

  • Primary: 192.168.33.29:8080
  • Secondary: 192.168.33.30:8080

まずブラウザで確認してみます。どちらのサーバーもGem in a Boxの画面が見れると思います。

スクリーンショット_2015-08-31_0_05_53.png

適当なGemをPushしてみましょう。

$ gem push pkg/ringc-0.1.2.gem --host http://192.168.33.29:8080
Pushing gem to http://192.168.33.29:8080...
Gem ringc-0.1.2.gem received and indexed.

ブラウザを更新するとGemがpushされたことが確認できます。

スクリーンショット_2015-08-31_0_23_53.png

Secondaryもrsyncによって同期されています。

スクリーンショット_2015-08-31_0_23_59.png

削除も同期されます。

スクリーンショット_2015-08-31_0_24_33.png

SecondaryからGemをインストールできることを確認してみます。

$ gem install ringc -s http://192.168.33.30:8080
Fetching: ringc-0.1.2.gem (100%)
Successfully installed ringc-0.1.2
Parsing documentation for ringc-0.1.2
Installing ri documentation for ringc-0.1.2
Done installing documentation for ringc after 0 seconds
1 gem installed

ばっちりですね!!:laughing:

解説

具体的な方法はGithubのコードを読んでいただくのが一番だと思います。
重要な一部だけ説明します。

SSH

  • gem_serverユーザを作成している
  • gem_serverユーザのホームディレクトリは/opt/gem_server
  • /opt/gem_server/.ssh以下をごにょごにょやって Primary -> Secondary にSSHできるようにしている

config.ru

Gem in a Boxを起動するconfig.ruは次のようになってます。

config.ru
require "geminabox"
require "rack/rsync"

if ENV["GEMINABOX_PRIMARY"] == ENV["GEMINABOX_MYADDRESS"]
  src = "/opt/gem_server/data/"
  dst = "#{ENV['GEMINABOX_SECONDARY']}:/opt/gem_server/data/"

  use Rack::Rsync, src, dst, "-a", "--delete" do |env|
    env["REQUEST_METHOD"] == "POST" || env["REQUEST_METHOD"] == "DELETE"
  end
end

Geminabox.data = "/opt/gem_server/data"
Geminabox.rubygems_proxy = true
run Geminabox
  • systemdが読む環境ファイルから自分のIPがPrimaryのIPと一致するかチェックする
  • 一致するときだけRack::Rsyncというミドルウェアを使うようにする
  • Rack::RsyncにリクエストメソッドがPOSTかDELETEのときだけrsyncを実行するように条件を与えている

systemd

systemdのunitファイルは次のようになってます。

[Unit]
Description=Gem Server

[Service]
WorkingDirectory=/opt/gem_server/geminabox
ExecStart=/usr/local/bin/unicorn -p 8080
EnvironmentFile=/opt/gem_server/geminabox/environments
User=gem_server
Group=gem_server

[Install]
WantedBy=multi-user.target

なんの変哲もないですね。起動ユーザをgem_serverユーザにして、環境ファイルを読むようにしてます。

Unicornが落ちたとき自動で復旧するようにRestart=alwaysとかしといてもいいかもしれません。

おわりに

タイミングシビアに同期したい場合にいかがでしょうか。
これだと不安だって場合はもっと下のレイヤーでやったほうがよさそうです。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2