rubyのRPMを作るのをDockerとCircleCIにやらせたら便利だった
この記事はフィードフォースエンジニア Advent Calendar 2015 - Adventar4日目です!
CentOSをメインで使っていると最新のrubyはyumで入らないのでどこかのリポジトリからインストールするか自前でビルドする必要があるかと思います。
プロビジョニングのタイミングでrbenvやmakeで入れてもいいのですがciや新規ホストを立てた時など毎回rubyのビルドをするので時間がかかってしまうんですよね。 それが嫌でrubyはリリースされるたびにrpm化してインストールするようにしています。 ところがrpmのビルド環境って用意するのが面倒だったりビルドしたい環境ごとにvagrantを立ち上げて〜とか意外と手間がかかる。
自動化したいなーと思っていて、Dockerは特定の環境を再現するのに最適だし、CircleCIはGitHubのpushやmergeをトリガーにしてあれこれできるし、これだ!っていうことでDockerとCircleCIを使って自動でビルドできるようにしました。
今回の記事で使ったソースコードはこちらです。
critical-alert/oreno-ruby-rpm · GitHub
全体の流れ
circle.ymlを見るとなんとなくわかると思いますが、流れとしては以下のような感じ。
- GitHubにpushまたはマージされたタイミングでCircleCIが走る
- docker build でdocker imageをビルドする
- docker run で実際にRPMビルドを行う
- docker cp でコンテナの中から出来たRPMを取り出す
RPMを取り出すと書いていますがこれは正確ではなく、$CIRCLE_ARTIFACTS
というパスにファイルを置くとCircleCiのArtifactsからDLできるようになっています。
ポイント
実際にRPMのビルドを行っているのはdocker run
で立ち上げたコンテナの中です。
肝心のDockerfileはこのようになっています。
FROM centos:6.6 MAINTAINER hirokazu SUGIUCHI # setup RUN yum update -y RUN yum install -y rpm-build tar # ruby depends RUN yum -y install readline-devel ncurses-devel gdbm-devel glibc-devel gcc openssl-devel libyaml-devel libffi-devel zlib-devel # builduser RUN useradd rpmbuilder # build prepare RUN mkdir -p /home/rpmbuilder/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} RUN chown rpmbuilder:rpmbuilder /home/rpmbuilder/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} WORKDIR /home/rpmbuilder/rpmbuild ADD http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.3.tar.gz SOURCES/ COPY ruby22x.spec SPECS/ruby22x.spec RUN chmod 777 /home/rpmbuilder/rpmbuild/SOURCES/* # build USER rpmbuilder CMD ["rpmbuild", "-ba", "/home/rpmbuilder/rpmbuild/SPECS/ruby22x.spec"]
いくつかポイントがるのでそれぞれ解説します。
rpmをbuildするuserを作った
通常Dockerはすべてrootユーザーでコマンドが実行されますが、rpmをビルドするときはrootでのビルドは推奨されません。(今回に限ればそれほど問題は無いと思いますが)
お作法に則ってrpmbuilder
という一般ユーザーを作成しています。
Dockerfile内でUSER
と指定すると、それ以降はその指定されたユーザーでコマンドが実行されます。
それより前でパーミッションの調整しているのはそのためです。
最後にCMDでrpmbuild
初期の段階では build.sh なるものを作ってそのなかでrubyのソースをダウンロードし、rpmbuild
コマンドを実行する、としていました。
そのあとdocker run時に docker run ruby-rpm /bin/sh ./build.sh
といったようにshスクリプトを実行させていました。
run時にスクリプトを叩くのはあるあるですが、そのようにしてしまうとそのスクリプトの中であれこれやってしまい何をやっているのかわかりづらくなるのと、スクリプト のメンテも必要になってしまうのでDockerfileで完結するならその方が良いと考えDockerfileのみで完結するようにしました。
Dockerfileの中にCMDがあることによってそのコンテナの役割が明確になるというメリットもあります。 そうすることで、このコンテナはrpmを作るバイナリとして扱えるようになったとも言えます。 (docker imageをrunするだけでrpmがポンとできるような...)
課題
複数のOSに対応していない。今回はCentOS6でビルドしましたが、CentOS7にも対応したいです。
これはDockerfileがDockerfile
という名前ではなくてもよくなったことを利用してDockerfileを複数(Dockerfile_centos6
とDockerfile_centos7
)作り、docker build時に指定しそれぞれ実行すれば作れそうです。
まとめ
これでrpmのspecファイルとrubyのソースURLをちょいっと変更してpushすればrubyのrpmが出来上がります。 12/25日にはrubyの2.3.0が公開されるはずなのでその時には活躍してもらおうと思っています!
というわけで、5日目の明日はフィードフォースのMaker、kasei_sanです!
参考リンク
大変参考にさせていただきました