この記事は KMC アドベントカレンダー 2016 の 12 日目です。前日は spi8823 の「Unityでパーティクルをドット絵風にするShader」 でした。 … 2 日遅れです。完全に登録してた事わすれとった。すいませんでした。休暇でシドニー放浪してました。
みなさんは Portage もとい Gentoo 使ってますか? 選択肢がたくさんあり、最新のパッケージがわりとすぐ降ってきて、その上何か足りないものがあっても ebuild 書いて転がしとくだけで気楽にインストールできるの便利ですよね。
わたしはサーバーで Gentoo を使うようになってからかれこれ 4〜5 年ほど経過しています。ただ、まあ、問題となるのは emerge にかかる時間と CPU リソースですよね。VPS の上での emerge -uDNav @world
は割と億劫だしつらいものになる。
今年の初めくらいからゆっくりとご家庭インフラの式年遷宮を進めるなかで、運用を見直してちゃんとうどん回せるようにしよう、と Binary package 導入をしてみたのでそのはなしです。
binary package
Portage には binary package 機能 が存在します (以後 binpkg)。Gentoo Wiki にある通り、複数のシステムがあるなかで何度もビルドするのは無駄・ハードウェアのスペックの問題でビルドが困難、といった状況をサポートするためにあります。便利ですね。これを使わない手はない。
そして実際 AWS t2.micro インスタンスで手元の base cookbook を完走させるには swap が数 GB 必要…。さらに 2 時間くらいかかる。なので、何かテストするためにインスタンス立ち上げてセットアップしてもやってられるかって感じである (AMI を作れという話はあるけれど)。つらい。
(余談だけれど、 https://github.com/sorah/gentoo-build というのがあって、systemd based Gentoo box を作るためのシェルスクリプト集と、AMI 作成用の packer script があってめちゃくちゃ便利です。)
binpkg の作り方
いくつかあります: quickpkg
コマンド、emerge --buildpkg
オプション、または FEATURES=buildpkg
。
quickpkg
はシステム上にインストールされたファイルをパッケージにしてくれます。この方法だと config を含めようともクリーンでない場合があるのでオススメできないとのこと (使ったことないから分からん)。
emerge
で FEATURES
あるいは --buildpkg
で渡した場合、ビルドディレクトリからパッケージ生成をするのである程度クリーンになります。
作例
これだけ。
sudo emerge --buildpkg dev-libs/boost
すると、 /usr/portage/packages/dev-libs/boost-1.56.0-r1.tbz2
とかが生成されます。メタデータである /usr/portage/packages/Packages
には、
BUILD_TIME: 1480786879
CPV: dev-libs/boost-1.56.0-r1
DEFINED_PHASES: compile configure install preinst prepare setup test
DEPEND: virtual/libiconv[abi_x86_64(-)] app-arch/bzip2[abi_x86_64(-)] sys-libs/zlib[abi_x86_64(-)] !app-admin/eselect-boost =dev-util/boost-build-1.56*
EAPI: 5
IUSE: context debug doc icu +nls mpi python static-libs +threads tools python_targets_python2_7 python_targets_python3_4 abi_x86_32 abi_x86_64 abi_x86_x32 abi_mips_n32 abi_mips_n64 abi_mips_o32 abi_ppc_32 abi_ppc_64 abi_s390_32 abi_s390_64
KEYWORDS: alpha amd64 arm ~arm64 hppa ia64 ~mips ppc ppc64 ~s390 ~sh sparc x86 ~ppc-aix ~amd64-fbsd ~x86-fbsd ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~sparc-solaris ~sparc64-solaris ~x86-solaris ~x86-winnt
LICENSE: Boost-1.0
MD5: b3720e94e07a32c7c076cec4c1c0207c
PROVIDES: x86_64: libboost_atomic.so.1.56.0 libboost_chrono.so.1.56.0 libboost_container.so.1.56.0 libboost_date_time.so.1.56.0 libboost_filesystem.so.1.56.0 libboost_graph.so.1.56.0 libboost_iostreams.so.1.56.0 libboost_locale.so.1.56.0 libboost_log.so.1.56.0 libboost_log_setup.so.1.56.0 libboost_math_c99.so.1.56.0 libboost_math_c99f.so.1.56.0 libboost_math_c99l.so.1.56.0 libboost_math_tr1.so.1.56.0 libboost_math_tr1f.so.1.56.0 libboost_math_tr1l.so.1.56.0 libboost_prg_exec_monitor.so.1.56.0 libboost_program_options.so.1.56.0 libboost_random.so.1.56.0 libboost_regex.so.1.56.0 libboost_serialization.so.1.56.0 libboost_signals.so.1.56.0 libboost_system.so.1.56.0 libboost_thread.so.1.56.0 libboost_timer.so.1.56.0 libboost_unit_test_framework.so.1.56.0 libboost_wave.so.1.56.0 libboost_wserialization.so.1.56.0
RDEPEND: virtual/libiconv[abi_x86_64(-)] app-arch/bzip2[abi_x86_64(-)] sys-libs/zlib[abi_x86_64(-)] !app-admin/eselect-boost
REQUIRES: x86_64: ld-linux-x86-64.so.2 libbz2.so.1 libc.so.6 libgcc_s.so.1 libm.so.6 libpthread.so.0 librt.so.1 libstdc++.so.6 libz.so.1
RESTRICT: test
SHA1: bebdceba8188c82399a7d655d97315c79a2a0454
SIZE: 9514605
SLOT: 0/1.56.0
USE: abi_x86_64 amd64 elibc_glibc kernel_linux nls python_targets_python2_7 python_targets_python3_4 threads userland_GNU
MTIME: 1480786898
REPO: gentoo
のように記録される。
あとは /usr/portage/packages
を http(s) で見えるところに置いて make.conf の PORTAGE_BINHOST
に書いて FEATURES=getbinpkg
を指定すれば、emerge
時に Packages
ファイルをとりにいって binpkg が利用可能であればそれを利用するようになる。
These are the packages that would be merged, in order:
Calculating dependencies... done!
[binary N ] dev-libs/boost-1.56.0-r1:0/1.56.0::gentoo USE="nls threads -context -debug -doc -icu -mpi -python -static-libs -tools" ABI_X86="(64) -32 (-x32)" PYTHON_TARGETS="python2_7 python3_4" 0 KiB
ちなみにわたしは S3 に置いてます。
binpkg の落とし穴
ビルド時の依存や USE フラグが異なると利用されません。
たとえば、 icu
USE flag がオンになっていない binpkg がある状態で、USE=icu emerge -av dev-libs/boost
とかするとこうなる。
Calculating dependencies... done!
[ebuild N ] dev-libs/icu-58.1-r1:0/58.1::gentoo USE="-debug -doc -examples -static-libs" ABI_X86="(64) -32 (-x32)" 0 KiB
[ebuild N ] dev-libs/boost-1.56.0-r1:0/1.56.0::gentoo USE="icu nls threads -context -debug -doc -mpi -python -static-libs -tools" ABI_X86="(64) -32 (-x32)" PYTHON_TARGETS="python2_7 python3_4" 0 KiB
Total: 2 packages (2 new), Size of downloads: 0 KiB
!!! The following binary packages have been ignored due to non matching USE:
=dev-libs/boost-1.56.0-r1 -icu
NOTE: The --binpkg-respect-use=n option will prevent emerge
from ignoring these binary packages if possible.
Using --binpkg-respect-use=y will silence this warning.
--binpkg-respect-use=n
や --binpkg-changed-deps=n
を指定すれば存在する binpkg を常に利用しようとするけれど、USEフラグは反映されることはない。依存はABI差で苦しんだり可能性があるってくらいかな。オススメはしない。
なお、複数の USE フラグのコンビネーションが想定される場合は FEATURES=binpkg-multi-instance
を有効にすると良い。 (make.conf(5) を参照)
最新 1 binpkg だけでなく、同じバージョンに対するビルドが複数保持されるようになります。そのかわりクリーニングは自分でやらなくちゃいけない、という感じ。
binpkg を便利に運用する
binpkg はうまく運用すれば便利ですが、実際の運用はどうするのが良いでしょう? というのがこのポストの本題です。
需要
emerge -uDNav @world
をばしばし実行したいので binpkg 最新のがいろいろ常にあってほしい- 依存が変わった・更新されたら binpkg 更新されてほしい
前者に関しては binpkg 実行するホスト用意して定期的に叩けば良い。
後者が問題で、そもそもサーバーやマシンによっては不要なパッケージハあるし、システムにインストールするのは微妙。 --buildpkgonly
でも結局 build dependencies は必要になってしまうのでこれもだめ。
そこで systemd-nspawn を使う
systemd-nspawn とは
systemd-nspawn は systemd が提供している、Linux の namespace を活用したコンテナを実行するためのツールです。シンプルでたいへん便利。init プロセスを実行させ、さらに個別の IP を持たせて実際の仮想マシンの用に使えるコンテナを作る事もできれば、単純に chroot では実現できないので namespace を使ってコマンドを実行したい、といういろんな需要に対応できます。
ArchWiki の systemd-nspawn 記事も参考にすると分かりやすいと思う。
これをいい感じに使うと binpkg 運用に便利ができそうです。自力で chroot するより楽なのもあるし。
やってみる (emerge -uDNv world)
./base
に stage3 を展開した物、 ./etc-portage
に /etc/portage
相当の物がある事が前提にしています (/etc/portage
と読み替えても良いが、コピーを推奨。理由は後述)
まずは emerge -uDNav @world
の場合。
sudo systemd-nspawn \
--register=no \
--bind=/usr/portage:/usr/portage \
--bind-ro=$(pwd)/etc-portage:/etc/portage \
--directory=./base \
emerge --buildpkg -uDNv @world
--bind
, --bind-ro
は Docker でいう -v
で、ホストシステムのディレクトリをコンテナ内でマウントできる。
/usr/portage
はバイナリパッケージが置かれるのと、そもそも repo もホストシステムのを使えば良いので書き込み可能で --bind
している。 /etc/portage
は書き込み不要なので --bind-ro
、という具合。
これで ./base
の上にある Gentoo システムを更新しつつ binpkg のビルドもされます。まずはこれを定期実行する事で上記需要の (1) は達成できます。既存のシステムと隔離されていることから、cron (あるいは systemd.timer) で実行する事もできるはず。
やってみる (個別にインストール)
./etc-portage/make.conf
に適切な PORTAGE_BINHOST
と FEATURES=getbinpkg
などを設定した上で、単純に下記のように実行すれば OK。
sudo systemd-nspawn \
--register=no \
--bind=/usr/portage:/usr/portage \
--bind-ro=$(pwd)/etc-portage:/etc/portage \
--directory=./base \
--ephemeral \
emerge --buildpkg -v dev-libs/boost
--directory
に実際にファイルの変更を反映させない状態でコンテナを起動できる --ephemeral
を指定するのが良いです。これでまずビルド用の rootfs は綺麗に保たれます (なお ./base
の親ディレクトリは btrfs である必要があります)。
また、ビルド環境の make.conf
で FEATURES=getbinpkg
を指定しておく事で、既にある binpkg が利用可能であればそれが利用されるし、なければ更新が走るという挙動になります (依存含めて)。これで (2) もおおむね達成できる。
ビルド環境の /etc/portage の管理
正直ここはまだ固まっていません。少なくとも、
- USE フラグ (
package.use
) は依存含めて揃えておく必要がある package.mask
,package.unmask
,package.accept_keywords
も揃えておいた方が良い、あるいはインストール時に個別に指定しておく
というのが必要です。
つまり、想定しうる全部の package.use
等に書かれる指定をビルド環境の /etc/portage
には含めておく必要がある。
全システムで一緒ならそれを使い回せばいいですが、実際場合によって USE フラグが異なる事もしばしばあるでしょう。たとえば net-analyzer/zabbix では agent
, server
, proxy
といった具合に、役割毎に必要となるフラグが違います。
わたしは /etc/portage
以下の変更を itamae と sorah/itamae-plugin-resource-portage で書き換えています。itamae レシピを解析をするのはややしんどそうなので、今のところは全サーバーから package.use
などを集めてきて手で整理してます。
(
for x in rin udzuki mio; do
ssh -T "${x}" 'sh -c "cat /etc/portage/package.use/*"'
done
) | sort --key 1 | uniq > /tmp/package.use
vim /tmp/package.use
cp /tmp/package.use ./etc-portage/package.use/hoge
なお先に例に上げた net-analyzer/zabbix を USE=server
としているサーバーはまだ式年遷宮されてない…。やるとしたら --ephemeral
で実行しつつ emerge 実行前に設定を変更するしかない。
こういうケースを含め、自動実行を踏まえて通知がいい感じに欲しいとか、いろいろ欲求がでてきているので、なんか便利なラッパーツールでも書くかぁ、という気持ちになっています。
まとめ
わたしの運用は現状こんな感じになってます。みなさんも Gentoo でたのしくインフラ運用しましょう。
ツールたぶんそのうち作ります (いや、リポジトリはもうあるんだけどまだいい感じになってません)。
UPDATE (Feb 27, 2017): つくった sorah/binpkgbot
なお、このアドベントカレンダーの翌日担当は walkure の「ゼネコン辞めた話」 だそうです。