こんにちは寺岡です。
この記事は TECHSCORE Advent Calendar 2014 の 3 日目の記事です。
それは遥か昔、世界が未だデプロイツールを手に入れる前のお話。
その頃のエンジニアにとって、デプロイは神聖かつ盛大な儀式であった。
儀式には決して破ってはいけない戒律が存在した。
「tar、scp、rakeなどの高度で難解な呪文を駆使し、正しい順序をもって執り行うべし」
戒律は地域、文化、宗教の違いによりさまざまなバリエーションが存在し、儀式の困難さに拍車をかける。
しかし、最後は必ずこう締めくくられるのだ。
「一度呪文を間違えたその時は、大いなる災厄がこの世を襲うであろう」と。
災厄を恐れたエンジニアたちは、長大で重厚な手順書を作り、なんとかデプロイを乗り切ろうとした。
それでもなお、手順書のバグやタイプミスにより悲劇は繰り返されるのであった…。
しかし、度重なる悲劇と切り戻しに疲弊したエンジニアたちに希望の光が現れた。
災厄を見かねて立ち上がった勇者たちが、デプロイに立ち向かう武器を手に帰ってきたのだ。
Capistranoをインストールした勇者たちが cap deploy という呪文を唱えると、たちどころにデプロイが実行されていった。
こうして大いなる災厄は過去のものとなり、世界は平和に包まれたのだ。
Capistrano V3 で使えなくなった deploy_via オプション
つい悪乗りしてしょうもない小話を書いてしまいましたが、今回はRailsの鉄板デプロイツール Capistranoのネタを書きます。
Capistranoは元々Railsに特化したデプロイフレームワークとして開発されました。
しかし、2013年にリリースされたv3からはRailsへの依存をなくした汎用的なデプロイツールとなりました。
この際、大幅なアーキテクチャ変更が行われたためv2から削られた機能は少なくありません。
その中のひとつがdeploy_viaオプションです。
このオプションのデフォルトは deploy_via :export で、デプロイ実行時は下記の図のように、
デプロイ先サーバが直接リポジトリのソースをチェックアウトすることになります。
LAN にリポジトリが存在する場合は、デプロイ先サーバからリポジトリに直接アクセスする必要があるためデプロイすることができません。
こんな場合はdeploy_via :copy と設定することで、デプロイ元で取得したソースをデプロイ先に転送することができたのです。
しかし、Capistrano v3 ではこの機能が削除されたためLAN上のリポジトリからデプロイするのは難しくなってしまいました。
本稿ではなんとかしてLAN上のプライベートリポジトリからデプロイする方法を検討してみたいと思います。
SSH Port Forwarding
ここで一旦話は飛んで、SSHのお話です。
SSHにはポートフォワーディングという機能があります。
以下の様に-Lオプションを指定することで、sshクライアント側のリッスンポートへのアクセスを
SSHトンネルを通ってリモートホスト側のネットワークへ転送することが出来ます。
1 |
ssh -L (クライアントの)リッスンポート:転送先ホスト:転送先ポート リモートホスト |
例えば、以下のような状況があったとします。
- WAN側のapp2サーバは80番ポートをリッスンしている
- 外部には開放されておらず、同一ネットワークのapp1からしかアクセスできない
- app1にはdeployからsshでアクセスできる
ここでLAN側のdeployサーバで以下のコマンドを実行すると、deploy:8080を通してapp2:80にアクセスすることが可能になります。
1 |
ssh -L 8080:app2:80 app1 |
SSH Remote Port Forwarding
SSHのポートフォワーディングはsshクライアント側のポートをリッスンし、リモートホスト側のネットワークに転送する機能でした。
これとは逆にリモートホスト側のポートをリッスンし、クライアント側のネットワークに転送する機能も存在します。
これはリモートポートフォワーディングと呼ばれ、-Rオプションで指定することができます。
1 |
ssh -R (リモートホストの)リッスンポート:転送先ホスト:転送先ポート リモートホスト |
今度は、以下のような状況を考えてみます。
- LAN側のrepoサーバは80番ポートをリッスンしている
- 外部には開放されておらず、同一ネットワークのdeployからしかアクセスできない
- app1にはdeployからsshでアクセスできる
この場合、LAN側のdeployサーバで以下のコマンドを実行すると、app1:8080を通してrepo:80にアクセスすることが可能になります。
1 |
ssh -R 8080:repo:80 app1 |
Capistrano v3 で SSH Remote Port Forwarding
そろそろオチが見えてきましたね。
そう、CapistranoのデプロイはSSHを使ってデプロイ先サーバのコマンドを実行しているのです。
SSH接続でリモートポートフォワーディングを利用して、デプロイ先サーバの特定のポートをプライベートリポジトリへと転送することができそうです。
Capistranoは内部的にnet-sshライブラリを使用しており、このライブラリはポートフォワード・リモートポートフォワードにちゃんと対応してくれています。
今回はCapistrano本体に手を入れることなくconfig/deploy.rbのカスタマイズでプライベートリポジトリからデプロイできるようにしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# config/deploy.rb # SSHトンネルを通してアクセスするので、ホスト名をlocalhostにする set :repo_url, 'git://localhost/path/to/repository.git' # デプロイ先の9418ポートをrepoの9418へ転送する set :remote_forwards, %w{9418:repo:9418} namespace :deploy do # remote_forwards 設定に基づいてポートフォワードを設定するタスク desc 'Starting remote port forwarding' task :remote_port_forwarding do remote_forwards = fetch(:remote_forwards, []) return if remote_forwards.empty? on roles(:all) do with_ssh do |ssh| remote_forwards.each do |arg| info " remote port forwarding #{arg} #{host}" args = arg.split(':').reverse args[0] = args[0].to_i if args[0] args[2] = args[2].to_i if args[2] ssh.forward.remote(*args) end ssh.exec!(':') #nop ssh.loop end end end # リポジトリへアクセスする check, updatingの前にポートフォワードを設定する before :check, :remote_port_forwarding before :updating, :remote_port_forwarding end |
バッチリデプロイできました!
今回の例はgitですが、理論的にはsvnでも上手く行くはずです。
ただし、httpの場合はHOSTヘッダなどの関係でうまくいかない場合があるのでご注意ください。