SREチームの@cubicdaiyaです。今回はDockerとMakeを利用したメルカリの自作RPMパッケージのビルド環境について紹介します。
メルカリの自作RPMパッケージ事情とVagrant、そしてDocker
メルカリの開発およびプロダクション環境では現在CentOS6と7を利用しており、随時CentOS7へ移行中です。そのため、自作RPMパッケージをビルドする際はCentOS6と7向けにそれぞれビルドしています。ビルドしたパッケージはyumリポジトリサーバにアップロードした後、必要に応じてyumでインストール、Ansibleのplaybook化を行います。
RPMパッケージの作成はSREチームのメンバーが行っており、各自のローカルマシン上において make {パッケージ名}
を実行するだけでCentOS6と7向けのRPMパッケージをビルドできる環境をDockerで構築しています。
make nginx # nginxのRPMパッケージをビルド make nginx-build # nginx-buildのRPMパッケージをビルド make gaurun # gaurunのRPMパッケージをビルド make slackboard # slackboardのRPMパッケージをビルド make cachectl # cachectlのRPMパッケージをビルド (etc)
これの良いところはMac上でもCentOS向けにRPMパッケージを作成できる点です。当初はVagrantを利用していましたが、最近Dockerを利用するように変更しました。Vagrant上でビルドするのと比べるとパフォーマンスが良く、特にコンテナの起動や破棄が高速なのが大きなメリットです。また、Cプログラムのコンパイルが以前よりも早く終わるようになりました。(もっとも最近はGoプログラムのRPMパッケージが多いのですが)
DockerとMakeを利用したRPMパッケージのビルドシステム
次にビルドシステムの中身を少しだけ紹介していきます。以下がそのレイアウトです。DockerfileとMakefile、各種ユーティリティを格納したscripts、そしてRPMパッケージをビルドする際に登場するおなじみのディレクトリ群(BUILD、SOURCES、SPECS、SRPMS)があります。
rpmbuild ├── BUILD ├── Dockerfile.centos6 ├── Dockerfile.centos7 ├── Makefile ├── README.md ├── RPMS ├── SOURCES │ ├── gaurun-0.6.0.tar.gz │ ├── nginx-1.11.3.tar.gz │ (etc) ├── SPECS │ ├── gaurun.spec │ ├── nginx.spec │ (etc) ├── SRPMS └── scripts ├── build.sh (etc)
DockerfileにはDockerコンテナ内でRPMパッケージのビルドを実行するユーザの追加や各種パッケージをビルドするのに必要なツールのインストール処理を記述します。
RUN useradd -u ${RPMBUILD_UID} rpmbuild RUN yum groupinstall -y "Development Tools" && yum install -y wget gcc gcc-c++ rpm-build # etc... # setup Go environment ADD scripts /root/scripts RUN /root/scripts/build-goruntime.sh # # etc... #
リポジトリに含めるファイル、含めないファイル
各ソースコードのアーカイブやSPECファイルのほかにserviceやinit.dのスクリプトもまとめて同じGitリポジトリで管理しています。一方でビルド済みパッケージはサイズが大きいので、リポジトリにはコミットしないで定期的にyumリポジトリサーバ上のパッケージをS3のバケットと同期するようにしています。
また、Goプログラムをビルドする際はできるだけ最新のバージョンのGoを利用するようにしているのですが、配布されているGoのビルド済みアーカイブ(e.g. go1.6.3.linux-amd64.tar.gz)は非常にサイズが大きいのでこちらもリポジトリには含めずにDockerイメージをビルドするタイミングでダウンロードするようにしています。
make {パッケージ名} でRPMパッケージをビルド
ターゲットを指定せずに make
を実行するとターゲット(パッケージ)の一覧が出力されます。
$ make To make rpm package, type "make <TARGET>" Available targets are cachectl gaurun nginx nginx-build (etc)
ターゲットを指定した場合はCentOS6と7向けにDockerを起動してビルドスクリプトを走らせます。(Dockerイメージのビルドやレジストリからのダウンロードは依存ターゲットの prepare-docker
で行っています)
OSLIST = centos6 centos7 $(TARGET): prepare-docker for OS in $(OSLIST) ; do docker run -it --rm -v $(CURDIR):/workspace rpmbuild:$$OS scripts/build.sh $@ $$OS ; # # processing miscellaneous tasks # done
実際にRPMパッケージのビルドを実行する上記の scripts/build.sh
では以下のようにrpmbuildコマンドを呼び出しています。
rpmbuild -bb SPECS/$TARGET.spec
ターゲット名とSPECファイルのサフィックスを一致させるのがポイントです。また、ファイルの存在確認をはじめ各種雑多な処理もここでラップしてMakefileの記述があまり複雑にならないようにしています。
まとめ
メルカリのRPM自作事情、DockerとMakeを利用したRPMのパッケージビルド環境について解説しました。
メルカリでのRPMパッケージ自作は2年ほど前にさくらのクラウド上のサーバで雑にrpmbuildをインストールするところから始まりましたが、その後まもなく怠惰な@shmorimo御大が「めんどくさい」と言ってVagrant上で make {パッケージ名}
(以下省略)するスクリプトを書いたところからコマンド一発でRPMパッケージをビルドできるようになりました。
そしてCentOS7への移行が本格化したタイミングで複数の環境向けにより素早くパッケージをビルドしたい需要が出てきたことからDockerへと移行しました。Vagrantの頃も十分便利でしたが、Dockerになってビルドがより高速になったのが移行の最大のメリットでした。