DRYな備忘録

Don't Repeat Yourself.

Cgoを使ったパッケージと「Docker as Bug Report/Reproduce」というOSS運用について

このエントリはGo2 Advent Calendar 2017 - Qiitaの4日目です。

2017年は、着実にエロリツイート先生として実績を上げた1年でした、otiai10です。WETな方もよろしくお願いします。

Cgoを使った文字認識ライブラリがv2になりました 🎉

Go言語には、Cgoという、Go言語からC/C++を叩いたり、C/C++からGoを叩いたりできる機能があります。(参考: Go言語のcgoことはじめ)

僕自身、Goを書き始めたぐらいから、有名な文字認識ライブラリであるTesseract-OCRのラッパーパッケージをGoで作っていたんですが、作り始めた当初はGoの知識もCの知識も無くて、ただのコマンドラッパー(つまりtesseractのbinへPATHが通ってる環境で、stdout/stdinのバッファを介する入出力)の部分がありました。これはかっこよくない。

ので、苦節3年この度、上記のcgoを使って、完全にTesseractのC++APIをブリッジする形に作り直しました 🎉

github.com

Cgoの使い方・その他詳細については、Goのアドベントカレンダーは今年「その4」まであるんで、きっと誰かがやると思うし、本エントリでは上記OSSの運用について、Dockerがめっちゃ便利やったで!という話をしたいと思います。Dockerって内部実装はGoだし、まあGoアドベントカレンダーでいいでしょ。

Cgoを使ったパッケージの「Portability」

Go言語の魅力のひとつに、Potability(可搬性)があります。Go言語はGo自身が依存するCのライブラリなどが無いため、たとえばマシンにPythonをインストールしようするときにBuild Failedになったりとかそういうことがなく、ダウンロードページからtar落としてきて展開してPATH通したら今日からあなたもGopherになれます。Enjoy!

また、Go内部に各プラットフォーム、たとばWindows用のコンパイラの実装を持つので、MacOS上でWindows用の実行ファイルを出力することもできる点もGoの魅力的な「可搬性」と言っていいでしょう。

ただしCgoお前は別だ。

GoConでもid:hajimehoshiさんがぼやいていました。当たり前っちゃ当たり前なんですが、Cgoで、GoのソースからC/C++を参照するときは、当然マシンにC/C++のオブジェクトファイル・ヘッダファイルがインストールされていて、ライブラリをインストールしたパスにLD_INCLUDE_PATHなどが通ってる必要があります。そして、OSSとして公開する以上「俺の環境で動かねえんだが」みたいなissueはボカスカ投げつけられてくるわけです。勉強になります。

Docker as Bug Report/Reproduce という運用

様々な環境から投げつけられるissueのつらさは、主に以下の2点に集約されるかと感じています

  1. 手元で再現できない
  2. 問題が切り分けられていない

1000歩譲って、1番は僕側がどうにかするとして、2番はissue reporter側の責任じゃね?って思うんですが、みなさんどうですか。だいたい今までのケースだと「お使いの環境はなんですか?uname -aとか、go envとかの出力ください」みたいなやりとりしてたんですが、これも結局かったるいので、もうどうせなら、

「動くバグをくれ」 🐛

という結論に達しました。どうやるか。できるんですよ。そう、Dockerならね。
"バグが動く環境" ごとDockerに固めてしまって、それをsubmitしてくれれば、Dockerfileを作る側、つまりissue reporterに問題の切り分けを強いることができるし、僕もデバグ環境が簡単に手に入って一石二鳥となります。

幸い、Travis-CIでもDockerをserviceとして利用することができるため、issue reportに使われたDockerfileをそのままCIに組み込んで「この環境で動くことは保証されました」という使い方ができ、これは便利です。

例)

FROM debian:lenny
# とかそういう古めのOS

# 再現のためのapt-getとかしてもらう
# 任意のバージョンのGoのインストール

ADD . ${GOPATH}/src/github.com/your/repo
ENTRYPOINT go test github.com/your/repo

たとえばこれなんかは、まあissue reporterではなく自分でDockerfile作ったんですが、見事にDockerfileで再現できて、最終的にDockerfileを修正することでテストもpassしたケースです。 Fail in CentOS6.7 / Tesseract3.02.02 / Golang1.7.6 · Issue #97 · otiai10/gosseract · GitHub

f:id:otiai10:20171203232626j:plain 動くバグは美味えぜ!

今後の課題

Dockerfileでテストケース書くの、基本的に良いことばっかりなんですが、唯一問題があって、CIのビルドが遅い。テストケースの数だけdocker buildしているので。

otiai10/gosseract - Travis CI

たいして厚みのあるパッケージじゃないのに、平均して30分ぐらいかかってます。これTravis-CI側にビルド済みイメージのキャッシュかなんか持たせることって可能ですか(それサポートするとディスクサイズやばそうだけど)? あるいは、Docker Hubを上手いこと使って、buildの必要が無ければpullで済ます、みたいな判定を入れるかかな。 ご意見・ご助言・ご鞭撻、お待ちしております。

まとめ

というわけで「俺の環境で動かねえんだが」というissueには「Dockerfileでくれ」みたいな殺伐さを醸し出していきましょう。*1

雑感

  • 最近Goで遺伝子解析ツールを書く仕事してる
    • 画像解析とか文字認識とかはいっさい関係ない
  • Go書いてると小さいパッケージをどんどん切り出しやすいので、GitHubリポジトリが無限に増える
  • 去年、個人開発程度のOCRサーバならHerokuに立てればいいじゃない - DRYな備忘録というのを書いたんですが、いつのまにかHerokuが任意のDockerをデプロイできるようになってて、最高。
    • そっちに移行した影響で、Deploy to Herokuボタンが死んでます。Heroku Container Registryというのを使ってるので、app.jsonに依存しないため。
  • 12月になった途端に忙しくて、師走の魔力って凄いなって思った

現場からは以上です。良いお年を! Happy Hacking

DRYな備忘録として