chef/omnibusとDockerでPythonランタイムとpypiライブラリをパッケージングする

# 前置き
かつてはomnibus-rubyという名前でRubyラインタイムとgemを各種OS向けにパッケージ[^1]提供するために開発されていた（？）[chef/omnibus](https://github.com/chef/omnibus)ですが、いつからか言語問わず[^2]使えるクロスプラットフォームなパッケージ作成フレームワークのような感じになっています。

とはいえ、ググってもそもそもomnibus自体使っている人が少ない上にRubyの例がほとんどという状況なのですが、今回Pythonランタイムとpypiライブラリをパッケージングする機会があったので情報を残しておこうと思います。

なお、実施にあたってはこちらを大いに参考にしました。ありがとうございます。
http://qiita.com/sawanoboly/items/0902a97ef71d99ad60a6

# ビルド環境の準備
こちらも上で紹介した記事と同様にDocker環境でビルドしていますが、今回も同様です。
ただ、Dockerイメージの作り方が少し違います（もっと手抜きですｗ）

まず、omnibusのビルド環境を作るには公式で用意されているomnibus cookbookを流すのが一番手っ取り早いです。
https://github.com/chef-cookbooks/omnibus

[omnibus](https://github.com/chef/omnibus)公式では`kitchen-vagrant`や`kitchen-docker`を利用したビルド環境の構築が紹介されていますが、CircleCI上でやりたいので`kitchen-vagrant`はNG、`kitchen-docker`はDockerではバッドプラクティスとされているSSHで繋ぐために`openssh`入れたりSSHログイン用の余計なユーザを作ったりとか色々余計なことをやらかすので、それを回避してできるだけDockerネイティブに使うために作った`kitchen-docker_cli`というTest-Kitchen Driverを使用します。
https://github.com/marcy-terui/kitchen-docker_cli

で、こんな感じの設定にします。

```yaml:.kitchen.yml
driver:
  name: docker_cli

transport:
  name: docker_cli

provisioner:
  name: chef_zero

platforms:
  - name: centos-6.6
    driver_config:
      image: centos:centos6.6
    run_list:
      - yum-epel::default
      - omnibus::default
      - omnibus-lamvery::yum_clean
  - name: ubuntu-14.04
    run_list:
      - apt::default
      - omnibus::default
      - omnibus-lamvery::apt_clean

suites:
  - name: default
    attributes:
      omnibus:
        build_user:          root
        build_user_group:    root
        build_user_home:     /root
        install_dir:         /opt/lamvery
```

`omnibus-lamvery::yum_clean`,`apt_clean`は少しでも容量減らすために`apt-get clean`, `yum clean all`しとこうっていうだけの、ほとんど意味のない悪あがきrecipeですｗ

これで、あとは以下を実行するだけです。

```shell-session
bundle exec kitchen converge
```

焼きあがったDockerコンテナと`docker ps`とかで確認し、Docker Hubに登録します。

```shell-session
docker commit <container-id> <username>/<image>
docker push <username>/<image>
```

こうしてできあがったのがコチラ
https://hub.docker.com/r/marcy/omnibus-ubuntu-14/
https://hub.docker.com/r/marcy/omnibus-centos-6/

これをCircleCI上で`docker pull`して`docker run`でビルドを実行する感じです。

# パッケージのビルド
以下、全部説明するとキリがないのでかいつまんで記述します。
全体像が分かるソースはコチラです。
https://github.com/willyworks/omnibus-lamvery/tree/master/omnibus
これをビルド用のDockerコンテナ起動(`docker run`)時に`-v`でマウントさせてビルドします。

## Python
特にビルド時のオプション等にこだわりがなければ、[omnibus-software](https://github.com/chef/omnibus-software)という色んなソフトウェアをomnibusでビルドするためのレシピ（？）の寄せ集めの中にPythonがあるので、`Gemfile`に含めて`bundle`すればそれが使えるはずです。

```rb:Gemfile
source 'https://rubygems.org'

gem 'omnibus', github: 'chef/omnibus'
gem 'omnibus-software', github: 'opscode/omnibus-software'
```

今回はオプションを変えたかったので手元で定義しました。
オプション指定時の注意事項としては、Python特有ではないのですが、omnibusはそれ単体で完結するフルスタックなパッケージを作るためのフレームワークなので、システムグローバルなライブラリ等に依存するとNGです。
ちなみにビルドの最後でチェックが走るのでそこでエラーになって終了します。
Pythonで言うと`--with-system-ffi`のようなconfigureオプションを指定するとNGとなります。

## PyPi + ライブラリ
これは簡単です。まず、以下のようにパッケージが提供するPythonを使って`pip`をインストールします。

```rb:software/pypi.rb
name "pypi"

build do
  command "curl -kL https://bootstrap.pypa.io/get-pip.py | #{install_dir}/embedded/bin/python"
  command "ln -fs #{install_dir}/embedded/bin/pip #{install_dir}/bin/pip"
end
```

そして、これによって出来上がる`pip`コマンドでライブラリをインストールしていけば良いだけです。

```software/lamvery.rb
name "lamvery"

build do
  command "#{install_dir}/embedded/bin/pip install lamvery==#{version}"
  command "ln -fs #{install_dir}/embedded/bin/lamvery #{install_dir}/bin/lamvery"
end
```

ちなみに、その時提供されている最新のライブラリのバージョンは以下のように取得できます。

```rb
require 'rexml/document'
require 'open-uri'

doc = REXML::Document.new(open('https://pypi.python.org/pypi?:action=doap&name=lamvery').read)
p doc.elements['rdf:RDF/Project/release/Version/revision'].text #=> "0.14.0"
```

# 成果物の配布
今回は[Bintray](https://bintray.com)にアップしました。参考にした例ではGo製のCLIクライアントを使用していますが、REST APIを直に叩いてやってみました。
BintrayのREST APIはシンプルで良い感じです。レスポンスコードがおかしかったりしてちょっとハマったりもしましたが。。。
今後も使うなら良い感じに使えるgemとかが見当たらなかったので、作ってみても良いかもしれない。

参考までにBintrayにアップする雑なrake taskのコードはこんな感じ。
https://github.com/willyworks/omnibus-lamvery/blob/master/Rakefile

# まとめ
この辺で何かやろうとするとけっこうな確率でsawanobolyさんが先にやってて助かる（再掲）



[^1]: `rpm`,`deb`等のこと。Mac向けの`pkg`やWin向けの`msi`も作れるらしいです（自分ではrpm,debしかやってない）
[^2]: むしろ言語含まなくてもOK