Docker を使ってパッケージング
これは NSEG Advent Calendar 2015 -Adventar 11 日目の記事です。
NSEG とは?
一言で言うと「長野の IT 勉強会」ということになりますが、勉強会と言ってもお堅いものではなく、長野県やコンピュータにかかわることを幅広く取り上げて、楽しんだり技術の向上を目指す、といった感じで活動しております。詳しくは nseg.jp もご覧ください。
これは何?
先日の「理論から学ぶデータベース実践入門」読書会スペシャルの懇親会で、「 Docker 周辺が盛り上がっているけど、実際業務に使ってたりする?」ということを聞かれたので、自分は「独自で RPM なんかのパッケージを作る時に便利に活用してますよ」ということを話したのですが、じゃあ具体的にどんなことをしているのか、というのを公開している h2o-rpm を元に書いてみようかな、という趣旨です。
Docker 自体はウェブアプリケーションなどの展開・運用にそのまま便利に使え、そちらが本筋とも思いますが、こんな使い方もできる、という一例にしてもらえれば幸いです。なお RPM の SPEC の書き方については、今回は省略します。
パッケージング作業の概要
そもそも RPM などのパッケージを作る時は、パッケージ対象のソースコードと関連ファイル、 SPEC ファイルのようなパッケージング手順が書かれたファイルなどが必要なわけですが、もう一つ、パッケージを作成する対象のディストリビューションのクリーンな環境が必要です。しかしながらこれを毎回物理サーバやいわゆる仮想マシン上でやっていると、メンテナンスが大変、起動に時間がかかる、など作業上のストレスがあっという間にたまることになるので、気軽に作成・削除のできる Docker でその部分をやろう、ということになります。かつての boot2docker や、最近では Docker Machine のおかげで、自分のマシン上でもお手軽に作業ができるのも利点です。
その場合のおおまかなパッケージ作成の流れは、
- パッケージ作成対象のディストリビューションの環境を整える
- パッケージ作成に必要なソースファイルなどを Docker コンテナに展開
- ビルド、パッケージングを行う
- できたパッケージを Docker コンテナから取り出す
となりますが、 h2o-rpm ではこれを Dockerfile
と Makefile
で記述して、
Dockerfile
を使ったイメージビルドを利用して、環境準備、ソース展開、ビルド、パッケージング- できあがったイメージからデータ取り出しのためだけのダミーコンテナを作ってパッケージを取り出し
という形に落とし込んで、 make
一つで自動でパッケージングできるようにしています。パッケージングまでの部分は docker run
でやるのも一つなのですが、イメージビルドを使うことにしたのは、
docker run
だと、ローカルファイルシステム・コンテナ間でデータの受け渡し方法を用意するのが面倒- 結局自動処理のためにシェルスクリプトを書くことになるのなら、
Dockerfile
の方がシンプルで見通しがよい Dockerfile
の各ステップの中間イメージが残っていくため、失敗した時の作業再開や調査がやりやすい
という理由によるものです。
以下、リポジトリの Dockerfile
のうち Dockerfile.centos7
と Makefile
を元に、この流れを見ていきます。
ビルド環境の準備
まず、ビルド環境の準備は Dockerfile
に記述してある
FROM centos:7
ENV HOME /
RUN yum update -y
RUN yum install -y rpm-build redhat-rpm-config rpmdevtools cmake gcc-c++ tar make openssl-devel ruby bison
RUN rpmdev-setuptree
RUN echo '%dist .el7' >> /.rpmmacros
で行っています。 centos7
の公式イメージを元に、環境内のパッケージの全アップデート、ビルド作業に必要なコンパイラなどのパッケージやビルド対象と依存関係にあるパッケージのインストール、ビルド用ディレクトリツリーの作成などを順に実施しています。
ENV HOME /
については、 RPM のパッケージング処理がホームディレクトリ配下の rpmbuild
ディレクトリを使う、という方式なのと、一般的なコンテナ内ではトップディレクトリが起点になっているというのを合わせる意味で指定しています。イメージ環境自体は使い捨てなので、通常やらないだろう設定をやってもいっこうに構いません。
ソース展開
ソースの展開は Makefile
の
cp Dockerfile.$* Dockerfile
tar -czf - Dockerfile rpmbuild | docker build -t $(IMAGE_NAME) -
と Dockerfile
の
ADD ./rpmbuild/ /rpmbuild/
の共同作業になります。
まず Makefile
の方ですが、各ディストリビューション用に Dockerfile.{dist}
のような形でを用意しているものを Dockerfile
にコピーした後、パッケージ用ファイルを配置してある rpmbuild
ディレクトリとともに tar.gz
形式にまとめ、それを docker build
に STDIN
経由で渡しています。このように渡されたファイルは、 tar.gz
内のディレクトリツリー構造そのままに Dockerfile
の ADD
や COPY
で参照できるので、これを使ってコンテナ内の /rpmbuild
以下にファイルを展開しています。
この辺りについては Docker の build コマンドのリファレンス、 Dockerfile のリファレンス に詳しいです。
また、最初にこの方式で書いた時期とは異なり、現在では .dockerignore
や docker build
の -f
オプションも使えますので、上記のような形ではなく、これらの機能を使うことでも同様のものを実現可能と思います。
ビルドとパッケージング
ここは Dockerfile
内の記述になりますが、
RUN rpmbuild -ba /rpmbuild/SPECS/h2o.spec
RUN tar -czf /tmp/h2o.tar.gz -C /rpmbuild RPMS SRPMS
CMD ["/bin/true"]
という形で、 RPM のビルド処理を行い、できあがったものを /tmp
以下に tar.gz
ファイルとしてまとめています。このアーカイブファイルは次の取り出しで参照します。
最後の CMD
行は、イメージから作成したコンテナを即時終了させるために便宜的に設定したものとなります。
パッケージの取り出し
ここは Makefile
側の処理になりますが、前のステップまででできあがったイメージを参照し、
docker run --name $(IMAGE_NAME)-tmp $(IMAGE_NAME)
mkdir -p tmp
docker wait $(IMAGE_NAME)-tmp
docker cp $(IMAGE_NAME)-tmp:/tmp/$(TARGZ_FILE) tmp
docker rm $(IMAGE_NAME)-tmp
のコマンドを順に処理して、 Dockerfile
で生成された tar.gz
ファイルを取り出しています。
まず docker run
で、 Dockerfile
で作成したイメージを元にダミーコンテナを起動しています。 Dockerfile
内で指定してある CMD ["/bin/true"]
により、このコンテナの実行は即座に終わります。欲しいのはダミーコンテナ内のファイルなので、コンテナが起動している必要はありません。念のため docker wait
を入れて、コピー処理の前にコンテナが停止しているのを確実にしています。
ダミーコンテナが停止した後は docker cp
を使い、コンテナ内のファイルをローカルのファイルシステムにコピーし、用が済んだダミーコンテナを docker rm
で削除しています。
欲しいファイルを取り出せたので、後は好きにこれを展開すればいいのですが、本リポジトリではこれを {dist}.build
のようなディレクトリ配下に展開しています。
まとめ
以上のような形で自動パッケージングを行い、ローカル側にコピーしてきているわけですが、もちろんできたものを直接特定のサーバにアップロードするなども自由自在です。対象もパッケージングに限らず、 Docker 環境中でなにかしらの成果物を作り、取り出す、という方法として一般的に使える、と思います。
また、ここでは単に好みから Makefile
を使っていますが、シェルスクリプトやバッチファイルでも構いません。かつては tar.gz
などのアーカイブファイルを渡さなければいけない以上、 Windows で実行するのは若干難しかったように思いますが、今では前述の .dockerignore
や docker build
の -f
オプションもあるので、敷居は下がったのではないかと思います。
さらに Windows Server 2016 では Docker がサポートされることも発表されていますので、 Windows アプリケーションも同じような手法でパッケージングできたりするのかもしれず、今後が楽しみです