capistrano等のデプロイツールでエラーに遭遇したらまずSSH周りを疑うようにしてる

今、職場でcapistranoというツールを使ってデプロイするRailsシステムを扱っている。
つい数週間前とくに問題なく実行完了できたデプロイ処理が、先日エラーで進められなくなっていた。

今回の記事では、その時の「２段構えのエラー」を完全に回避する解に辿り着くまでの顛末を書いておく。
なお、解決の要点は次の２点だった。

* Gemfile へ次の２つを追加して bundle install
    * `gem 'ed25519'`
    * `gem 'bcrypt_pbkdf'`
* net-sshパッケージを 5.0.2 から 5.2.0 へアップデート
    * `$ bundle update net-ssh`

## 最初の伏兵と遭遇

```
$ bundle exec cap production deploy

～～省略～～

00:01 deploy:check:directories
      01 mkdir -p /var/www/hoge/shared /var/www/hoge/releases
verify_host_key: :secure is deprecated, use :always
(Backtrace restricted to imported tasks)
cap aborted!
NotImplementedError: OpenSSH keys only supported if ED25519 is available
net-ssh requires the following gems for ed25519 support:
 * ed25519 (>= 1.2, < 2.0)
 * bcrypt_pbkdf (>= 1.0, < 2.0)
See https://github.com/net-ssh/net-ssh/issues/565 for more information
Gem::LoadError : "ed25519 is not part of the bundle. Add it to your Gemfile."
```

このエラー自体は対処についての情報がメッセージ中にあり、ネット上にも下記のようにピンポイントな記事があってありがたかった。

> Capistranoデプロイしようとしたら突然ed25519エラーが出たときの解消法
> https://qiita.com/Tosh39/items/7bac004d821299cc4f6b

Gemfileに次を追加して bundle install で解消である。

```diff:Gemfileのdiff
+  gem 'ed25519'
+  gem 'bcrypt_pbkdf'
```

ところが。

## 伏兵は２段構えだった

その後デプロイコマンドを再実行すると、エラー内容が変わった。

```
00:01 deploy:check:directories
      01 mkdir -p /var/www/hoge/shared /var/www/hoge/releases
verify_host_key: :secure is deprecated, use :always
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as dep-user@192.168.55.31: expected 64-byte String, got 3

```

もちろん、事前に下記でSSH接続が成功するように .ssh/config 等の設定は済ませ、接続確認もしてある。
`$ ssh dep-user@192.168.55.31`

Rails や capistrano というツール自体は今回のプロジェクトで初めて触るので良くは知らないのだが、以前 ansible を使った時に「つくづくSSH周りのことを紐解かないといけないエラーばっかり出遭うな」という体験をしていたので、この件もまずはSSH周りの設定を疑ってみた。

## まずは ssh-agent 周り

ssh-agent の挙動が影響しないか探ってみることにし、下記の記事などを情報源にアレコレやってみた。

> capistranoでデプロイする時のssh-key周りのTips
> https://qiita.com/Takkiii/items/c4f2cfd143e97e061e66

> ssh-add で Could not open a connection to your authentication agent が出るときの対処法
> https://qiita.com/ytheta/items/cbbd0b833c19784dfa1e

が、今回の件とは関係無さそうだった。

## SSHKitを単体で使ってみる

そこで「最終的にSSH接続を実行しているライブラリ単体で動かせるかどうかを確かめよう」と考えた。
下記の記事等の情報源から、SSHkit を単体で使う実験をしてみた。

> sshkitを単体で使ってみる
> https://qiita.com/cazador/items/2d675f911b23d4fed160

> SSHKitを実際に使ってみて理解する
> http://48n.jp/blog/2016/06/14/learn-ssh-kit/

簡易なスクリプトを作って、

```ruby:hoge.rb
$ cat hoge.rb
require 'sshkit'
require 'sshkit/dsl'
require 'ed25519'
require 'bcrypt_pbkdf'
include SSHKit::DSL

SSHKit.config.output_verbosity = Logger::DEBUG

remote_host = SSHKit::Host.new('192.168.55.31')
remote_host.user = "dep-user"
remote_host.ssh_options = {
    verify_host_key: :always,
    keys: %w(~/.ssh/hoge_rsa),
    forward_agent: true,
    auth_methods: %w(publickey)
}

on remote_host do
  execute :ls
end
```

これを実行してみるも、どうも的を射たメッセージが得られない。

```bash
$ bundle exec ruby hoge.rb
  INFO [86e23d74] Running /usr/bin/env ls as dep-user@192.168.55.31
 DEBUG [86e23d74] Command: /usr/bin/env ls
/home/hoge/repo/vendor/bundle/ruby/2.4.0/gems/sshkit-1.17.0/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as dep-user@192.168.55.31: Authentication failed for user dep-user@192.168.55.31 (SSHKit::Runner::ExecuteError)
        from /home/hoge/repo/vendor/bundle/ruby/2.4.0/gems/sshkit-1.17.0/lib/sshkit/runners/parallel.rb:11:in `block (2 levels) in execute'
```

とはいえ、
これによって capistrano そのもののことは考えなくていいことはわかった。

## 改めて capistrano 実行時のエラーを眺めてみる

手がかりになりそうなキーワードを求めてアレコレ眺めていて、
`expected 64-byte String, got 3`
でググってみたらこんな検索結果が出てきた。

> Error &quot;expected 64-byte String, got 3&quot; · Issue #442 · capistrano/sshkit
> https://github.com/capistrano/sshkit/issues/442

英語のやり取りを頑張って読み進めていったら
> `Upgrading net-ssh from 5.0.2 to 5.1.0 solved this issue for me.`

やっと光が見えた！

## 問題を完全回避できた

晴れて、下記にて net-ssh をアップデートすることで net-sshパッケージが 5.0.2 から 5.2.0 へアップデートされ、
先程の hoge.rb もエラー無く実行できるようになった。

```
$ bundle update net-ssh
```

そしてこれによって capistrano の処理も初期処理から先へ話が進んだ。

## capistrano のことはやっぱりまだよく知らない

以上は「capistrano によるデプロイがエラーでコケる」という事態に対処した記録ですが、
最初から「sshコマンドではイケているユーザによるSSHログインがcap処理では通らない」ということに目星を付けたので、 capistrano そのもののことはほとんど調べることなくひたすらSSHのトラブルシュートで乗り越えました。

capistrano なり ansible なり、SSHを介してデプロイを行うツールを使う際は、事前にssh周りの知識を脳内整理しておいた方がいいと思います！
今回の記事は以上です。