dokku公式Vagrantfileでdokkuを試してみたメモ

# はじめに

dokkuについては、ローカルでherokuライクな環境が作れる、bashで書かれている、という情報を知っていただけで、一度も試してなかったので遅まきながら試してみました。
下記の記事を参考にしました。ありがとうございます！

* [VirtualBoxでdokkuを試した - @znz blog](http://blog.n-z.jp/blog/2013-11-15-dokku.html)
* [docker - Vagrantでdokkuを動かす - Qiita](http://qiita.com/hokaccha/items/af109d4037fa1d788617)

[progrium/dokku](https://github.com/progrium/dokku)公式レポジトリ内にVagrantファイルが用意されていたので、それを使えば簡単でした。
READMEに"Note: There are known issues with docker and Ubuntu 13.10 (1, 2) - use of 12.04 is recommended until these issues are resolved."とあるので、12.04で試しました。Vagrantfile内のデフォルトは13.04になっているので、環境変数を指定して以下のように実行しました。

# dokku環境構築の操作手順

```shell-session
git clone https://github.com/progrium/dokku
BOX_NAME=precise BOX_URI=http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box vagrant up
```

VirtualBoxのbox名をpreciseにして、BOX_URIは[A list of base boxes for Vagrant - Vagrantbox.es](http://www.vagrantbox.es/)にある"Official Ubuntu 12.04 daily Cloud Image amd64 (VirtualBox 4.1.12)"のURIを指定しています。

IPアドレスは環境変数DOKKU_IPで指定可能ですが、ここではVagrantfile内でデフォルトとして指定されている10.0.0.2をそのまま使いました。

```text:Vagrantfile
DOKKU_IP = ENV["DOKKU_IP"] || "10.0.0.2"
```

vagrant upはMacBook Air 2013だと約11分で終わりました。途中のログを見るとlxc-docker-0.10.0というパッケージを入れていました。

```shell-session
$ vagrant ssh
Welcome to Ubuntu 12.04.4 LTS (GNU/Linux 3.2.0-60-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Sat Apr 12 14:45:11 UTC 2014

  System load:  0.0               Users logged in:        0
  Usage of /:   5.4% of 39.37GB   IP address for eth0:    10.0.2.15
  Memory usage: 24%               IP address for eth1:    10.0.0.2
  Swap usage:   0%                IP address for docker0: 172.17.42.1
  Processes:    69

  Graph this data and manage this system at:
    https://landscape.canonical.com/

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

vagrant@dokku:~$
```

以下のコマンドでVHOSTファイルを作成します。

```
vagrant@dokku:~$ echo deploy.10.0.0.2.xip.io | sudo tee /home/dokku/VHOST
deploy.10.0.0.2.xip.io
```

vagrantの公開鍵も既に設置してくれています。

```
vagrant@dokku:~$ cat .ssh/authorized_keys
# CLOUD_IMG: This file was created/modified by the Cloud Image build process
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
```

あとはsshcommandを使ってvagrantの公開鍵をdokku用に加工して~dokku/.ssh/authorized_keysを作成します。

```shell-session
vagrant@dokku:~$ grep ^ssh-rsa ~/.ssh/authorized_keys | sudo sshcommand acl-add dokku vagrant
dd:3b:b8:2e:85:04:06:e9:ab:ff:a8:0a:c0:04:6e:d6
```

# サンプルアプリをデプロイしてみるテスト

## ssh接続設定

ホスト(OSX)の~/.ssh/configには以下の設定を追加しました。

```text:~/.ssh/config
ExitOnForwardFailure yes
Ciphers aes256-ctr,aes192-ctr,aes128-ctr
HashKnownHosts no
NoHostAuthenticationForLocalhost yes
ServerAliveInterval 30
Host my-heroku
  Ciphers arcfour256,arcfour128
  HostName 10.0.0.2
  User vagrant
  Port 22
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile ~/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
```

## サンプルアプリの取得とデプロイ

ホスト(OSX)で以下のコマンドを実行します。git-remoteのホスト名の部分(my-heroku)は上で~/.ssh/configに追加した設定のHostの値に合わせます。@の前のユーザ名はdokkuにします。コロンの後はデプロイしたい時のアプリケーション名です。

```shell-session
$ git clone https://github.com/heroku/node-js-sample
$ cd node-js-sample
$ git remote add deploy dokku@my-heroku:node-js-sample
$ git push deploy master
```

実行すると以下の様なログが出力されました。

```
Warning: Permanently added '10.0.0.2' (RSA) to the list of known hosts.
Counting objects: 319, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (274/274), done.
Writing objects: 100% (319/319), 201.92 KiB | 0 bytes/s, done.
Total 319 (delta 17), reused 319 (delta 17)
-----> Cleaning up ...
-----> Building node-js-sample ...
remote: warning: You appear to have cloned an empty repository.
remote: HEAD is now at 2e52ce7... Update documentation links
       Node.js app detected
-----> Requested node range:  0.10.x
-----> Resolved node version: 0.10.26
-----> Downloading and installing node
-----> Writing a custom .npmrc to circumvent npm bugs
-----> Installing dependencies
       npm http GET https://registry.npmjs.org/express
       npm http 200 https://registry.npmjs.org/express
(…略…)
-----> Deploying node-js-sample ...
=====> Application deployed:
       http://node-js-sample.deploy.10.0.0.2.xip.io
```

git remoteのURLで指定したコロンの後のアプリケーション名がサブドメイン名として使用されます。

curlでアクセスしてみると、無事アプリケーションからのレスポンスが返ってきました。

```shell-session
$ curl http://node-js-sample.deploy.10.0.0.2.xip.io
Hello World!
```

# コマンドの実行

## ゲストVM上で実行する場合

```dokku run アプリケーション名 コマンド 引数```として実行します。
[progrium/dokku](https://github.com/progrium/dokku#run-a-command-in-the-app-environment)に説明があります。

例


```shell-session
vagrant@dokku:~$ dokku run node-js-sample ls -lah
total 52K
drwxr-xr-x  6 root root 4.0K Apr 12 17:30 .
drwxr-xr-x 42 root root 4.0K Apr 12 17:43 ..
-rw-r--r--  1 root root   13 Apr 12 17:30 .gitignore
drwxr-xr-x  2 root root 4.0K Apr 12 17:30 .heroku
-rw-r--r--  1 root root    4 Apr 12 17:30 .npmrc
drwxr-xr-x  2 root root 4.0K Apr 12 17:30 .profile.d
-rw-r--r--  1 root root   37 Apr 12 17:30 .release
-rw-r--r--  1 root root   17 Apr 12 17:30 Procfile
-rw-r--r--  1 root root 1.2K Apr 12 17:30 README.md
drwxr-xr-x  4 root root 4.0K Apr 12 17:30 node_modules
-rw-r--r--  1 root root  561 Apr 12 17:30 package.json
drwxr-xr-x  3 root root 4.0K Apr 12 17:30 vendor
-rw-r--r--  1 root root  275 Apr 12 17:30 web.js
```

## ホスト(OSX)上で実行する場合

```ssh -t dokku@my-heroku run アプリケーション名 コマンド 引数```として実行します。
[progrium/dokku](https://github.com/progrium/dokku#remote-commands)に説明があって、sshに-tオプションを付けることを強くおすすめしますと書かれています。

例

```
$ ssh -t dokku@my-heroku run node-js-sample ls -lah
Warning: Permanently added '10.0.0.2' (RSA) to the list of known hosts.
total 52K
drwxr-xr-x  6 root root 4.0K Apr 12 17:30 .
drwxr-xr-x 42 root root 4.0K Apr 12 17:45 ..
-rw-r--r--  1 root root   13 Apr 12 17:30 .gitignore
drwxr-xr-x  2 root root 4.0K Apr 12 17:30 .heroku
-rw-r--r--  1 root root    4 Apr 12 17:30 .npmrc
drwxr-xr-x  2 root root 4.0K Apr 12 17:30 .profile.d
-rw-r--r--  1 root root   37 Apr 12 17:30 .release
-rw-r--r--  1 root root   17 Apr 12 17:30 Procfile
-rw-r--r--  1 root root 1.2K Apr 12 17:30 README.md
drwxr-xr-x  4 root root 4.0K Apr 12 17:30 node_modules
-rw-r--r--  1 root root  561 Apr 12 17:30 package.json
drwxr-xr-x  3 root root 4.0K Apr 12 17:30 vendor
-rw-r--r--  1 root root  275 Apr 12 17:30 web.js
```

## デプロイしたアプリケーションを削除する

[progrium/dokku](https://github.com/progrium/dokku#removing-a-deployed-app)に説明があります。
ゲストVM上で```dokku delete アプリケーション名```とすればOKです。

使い方の説明は以上です。


# 試行錯誤のメモ

## dokkuユーザのauthorized_keysを作る手順でハマった

参考にしたブログ記事ではゲストVMで

```shell-session
vagrant@dokku:~$ cat ~/.ssh/authorized_keys | sudo sshcommand acl-add dokku vagrant
```

と実行するとか、

[dokkuのREADMEのConfiguring](https://github.com/progrium/dokku#configuring)にはホスト(OSX)側で以下の様なコマンドを実行するとか書いてあります。

```
$ cat ~/.ssh/id_rsa.pub | ssh progriumapp.com "sudo sshcommand acl-add dokku progrium"
```

cat ~/.ssh/id_rsa.pubの部分は、ここではvagrantの公開鍵を出力することになります。
以下のようにして秘密鍵から生成するか

```
$ echo `ssh-keygen -yf ~/.vagrant.d/insecure_private_key` vagrant insecure public key
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
```

[Vagrantのレポジトリのkeysディレクトリ](https://github.com/mitchellh/vagrant/tree/master/keys)から以下のどちらかのコマンドで公開鍵を取ってきます。

```
$ wget -q -O - https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub
```

```
$ curl -3 https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub
```

curlのほうは-3オプション無しで試すと以下の様なエラーが出ました。

```
curl: (35) error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:reason(1112)
```

[2011:10:07:error_14077458_ssl_routines_ssl23_get_server_hello_reason_1112 \[carnivore news\]](http://carnivore.it/2011/10/07/error_14077458_ssl_routines_ssl23_get_server_hello_reason_1112)で-3オプションをつければ大丈夫と書いてあって、試すとOKでした。

```
| ssh progriumapp.com "sudo sshcommand acl-add dokku progrium"
```

の部分は今回の構成では、

```
| ssh my-heroku "sudo sshcommand acl-add dokku vagrant"
```

となります。


まとめると、ホストで実行するなら

```
$ echo `ssh-keygen -yf ~/.vagrant.d/insecure_private_key` vagrant insecure public key | ssh my-heroku "sudo sshcommand acl-add dokku vagrant"
```

とすればOKです。

でも、ゲストの~vagrant/.ssh/authorized_keysに公開鍵があるので、そんな面倒なことをしなくてもゲストで

```shell-session
vagrant@dokku:~$ cat ~/.ssh/authorized_keys | sudo sshcommand acl-add dokku vagrant
```

が楽だと思って、当初はこれを試したのですが、これでハマりました。コマンド自体は問題なく終わるのですが、```git push deploy master```でのデプロイ時にnode-js-sampleというレポジトリは無いという主旨のエラーが出てデプロイ出来ませんでした。

そこでゲストでsshcommandの在り処を調べてvimで確認してみました。

```
vagrant@dokku:~$ sudo which sshcommand
/usr/local/bin/sshcommand
```

以下は/usr/local/bin/sshcommandの抜粋です。

```
  acl-add) # sshcommand acl-add <user> <identifier>
    if [[ $# -ne 3 ]]; then
      echo "Usage : sshcommand acl-add user identifier"
      exit -1
    fi
    USER="$2"; NAME="$3"

    getent passwd $USER > /dev/null || false
    USERHOME=$(bash <<< "echo ~$USER")

    KEY=$(cat)
    FINGERPRINT=$(ssh-keygen -lf /dev/stdin <<< $(echo $KEY) | awk '{print $2}')
    KEY_PREFIX="command=\"FINGERPRINT=$FINGERPRINT NAME=$NAME \`cat $USERHOME/.sshcommand\` \$SSH_ORIGINAL_COMMAND\",no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding"
    echo "$KEY_PREFIX $KEY" >> "$USERHOME/.ssh/authorized_keys"
    echo $FINGERPRINT
    ;;
```

メインとなるのは

```echo "$KEY_PREFIX $KEY" >> "$USERHOME/.ssh/authorized_keys"```

の部分です。

~vagrant/.ssh/authorized_keysの内容は以下のようになっています。これが上の行の$KEYSに展開されます。

```
vagrant@dokku:~$ cat ~/.ssh/authorized_keys
# CLOUD_IMG: This file was created/modified by the Cloud Image build process
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
```

すると生成される~dokku/.ssh/authorized_keysは以下のようになってしまいます。

```text:/home/dokku/.ssh/authorized_keys
command="FINGERPRINT=is NAME=vagrant `cat /home/dokku/.sshcommand` $SSH_ORIGINAL_COMMAND",no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding # CLOUD_IMG: This file was created/modified by the Cloud Image build process
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
```

~/.ssh/authorized_keysの先頭にコメント行があるせいで、command=の行とssh-rsaの行の2行に分かれてしまっています。
正しい結果を得るには

```
grep ^ssh-rsa ~/.ssh/authorized_keys | sudo sshcommand acl-add dokku vagrant
```

のようにしてssh-rsaの行だけをsshcommandの標準入力に渡す必要があります。すると~dokku/.ssh/authorized_keysは以下のようになります。

```
command="FINGERPRINT=dd:3b:b8:2e:85:04:06:e9:ab:ff:a8:0a:c0:04:6e:d6 NAME=vagrant `cat /home/dokku/.sshcommand` $SSH_ORIGINAL_COMMAND",no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
```

ssh-rsaの前にcommandを指定すると、この公開鍵で認証されると指定のコマンドが実行されるようになります。[sshd(8): OpenSSH SSH daemon - Linux man page](http://linux.die.net/man/8/sshd)のAUTHORIZED_KEYS FILE FORMATのセクションに説明があります。$SSH_ORIGINAL_COMMANDの部分はクライアントがsshコマンドを実行した時のコマンドと引数が展開されるらしいです。

ゲストで/home/dokku/.sshcommandの中身を確認すると

```shell-session
vagrant@dokku:~$ sudo cat /home/dokku/.sshcommand
/usr/local/bin/dokku
```

となっていて結局/usr/local/bin/dokkuスクリプトが実行されることがわかります。

## デプロイ時にdokkuコマンドがどのように呼び出されるか調べてみた

git pushを実行した時にdokkuコマンドがどう呼ばれるのか、よくわかってないことに気づいたので、


```
vagrant@dokku:~$ sudo vim /usr/local/bin/dokku
```

を実行して

```
#!/usr/bin/env bash
echo `date +%Y-%m-%dT%H:%M:%S%z` "$@" >> /tmp/dokku.log # ←この行を追加
```

のようにデバッグ出力の行を追加してみました。

ホスト側で以下のようにして再度デプロイを試しました。

```shell-session
$ echo '' > README.md
$ git commit -a -m 'dummy modification to test deploy'
[master a069873] dummy modification to test deploy
 1 file changed, 1 insertion(+), 34 deletions(-)
 rewrite README.md (99%)
$ git push deploy master
```

その後ログを確認してみました。

```shell-session
vagrant@dokku:~$ cat /tmp/dokku.log
2014-04-12T18:54:22+0000 git-receive-pack 'node-js-sample'
2014-04-12T18:54:22+0000 git-hook node-js-sample
2014-04-12T18:54:22+0000 receive node-js-sample
2014-04-12T18:54:22+0000 cleanup
2014-04-12T18:54:22+0000 build node-js-sample
2014-04-12T18:54:41+0000 release node-js-sample
2014-04-12T18:54:41+0000 deploy node-js-sample
2014-04-12T18:54:41+0000 url node-js-sample
```

これはdokkuというよりgitの話でした。
ProGitの[Git - トランスファープロトコル](http://git-scm.com/book/ja/Git%E3%81%AE%E5%86%85%E5%81%B4-%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B9%E3%83%95%E3%82%A1%E3%83%BC%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB)の「スマートプロトコル」の「データのアップロード」のセクションに説明があります。英語版だと[Git - Transfer Protocols - The Smart Protocol](http://git-scm.com/book/en/Git-Internals-Transfer-Protocols#The-Smart-Protocol)です。

## xip.ioを使わないとどうなるか試してみた

[xip.io: wildcard DNS for everyone](http://xip.io/)はbar.10.0.0.2.xip.ioやfoo.bar.10.0.0.2.xip.ioのように、xip.ioの前にIPアドレスをつけてその前にサブドメインをつけたドメインを指定のIPアドレス（この例では10.0.0.2）に名前解決してくれるカスタムDNSサーバーです。

これを使わずにIPアドレスのみをVHOSTファイルに指定するとどうなるのか試してみました。

```
vagrant@dokku:~$ echo 10.0.0.2 | sudo tee /home/dokku/VHOST
```

この状態でデプロイすると、ログにデプロイされたアプリケーションのURLは以下のように出力されました。

```
=====> Application deployed:
       http://node-js-sample.10.0.0.2
```

node-js-sample.10.0.0.2のようにアプリケーション名がサブドメインとしてつけられるので、これではアクセスできないというわけです。xip.ioを使えばこの問題を解決できるんですねー、すごい！

ちなみにVHOSTの設定を忘れていた時はデプロイされたアプリケーションのURLは以下のようになりました。

```
=====> Application deployed:
       http://dokku:49153
```


# 感想

仕組みは興味深くて勉強になりました。が、私は普段使いにする気はないです。というのも開発中にちょこちょこ試すために毎回commitするのは面倒だから。今回の実験中ですら面倒でした。アプリケーション開発には[VagrantのSynced Folders](http://docs.vagrantup.com/v2/synced-folders/index.html)のほうが楽でいいです。

あと、[READMEのThings this project won't do](https://github.com/progrium/dokku#things-this-project-wont-do)を見ると複数台対応はdokkuのスコープ外で、その用途には[Flynn - The product that ops provides to developers](https://flynn.io/)をどうぞとのことです。