本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはcpanm、Plackの作者としても知られる宮川達彦さんで、テーマは
<前回
CarmelとCartonの違い
この節では、CartonとCarmelの動作の違いや、Carmelを利用するメリットなどを紹介します。
Cartonの動作原理
Cartonの動作原理を簡単におさらいします。
Cartonは内部的にcpanm
コマンドの--local-lib-contained
オプションを利用し、cpanfile
で指定されたモジュールをlocal
ディレクトリ以下にすべてインストールします。そのあと、インストールされたバージョンをlocal
ディレクトリ内からスキャンしてcpanfile.
ファイルに書き出します。
local
ディレクトリはcpanm
コマンドのインストール対象としてのみ指定されており、Cartonはこのディレクトリを直接管理することはありません。このため、複数ユーザーが異なるマシンでCartonを利用した場合、マシン間で同じ状態が維持されている保証はありません。
Carmelの動作原理
Carmelは内部的にcpanmコマンドのオプションなどは利用せず、cpanfileを読み込み、必要となるモジュールを再帰的に取得し、そのモジュールをCarmelリポジトリ[1]以下にディレクトリを作成して保持します。
これらのモジュールはCartonのようにlocal
ディレクトリ以下にインストールされることはありません。
$HOME/.carmel/5.20.1-darwin-2level/builds Plack-1.0048/ blib/ arch/ lib/ Plack-1.0050/ blib/ arch/ lib/
install
、list
、exec
などのサブコマンドを実行すると、Carmelはこのリポジトリをスキャンして、指定された条件にマッチする、あるいは固定されたバージョンのディレクトリを探し、モジュール名とディレクトリのマッピングを作成します。実際にPerlがモジュールをロードするときuse Plack;
)、ここではマッチするディレクトリ<Carmelリポジトリ>/Plack-1.
以下の/lib/
がロードされます。これには、Perlのインクルードパスをオーバーライドして、必要なファイルが固定されたディレクトリからロードされるようにしています。
ユーザーから見た違い
はじめに紹介したとおり、CartonとCarmelのコマンドには互換性や同等の別名コマンドが用意されているため、すでにCartonを利用しているみなさんは、ほとんどワークフローを変更することなく、Carmelを利用し始めることができます。また、Carmelを使ってみて何か問題があれば、Cartonに戻ることも簡単です。すでにCartonを使っていて特に問題がない場合でも、Carmelを試してみる価値はあります。この項では、Cartonを利用して問題になりやすいケースを紹介します。
たとえば、Mojoliciousのみに依存したアプリケーションを作ったとします。cpanfile
を作成して、Cartonを利用して依存モジュールを管理してみましょう。
> carton install Installing modules using /path/to/carton-demo/cpanfile Successfully installed Mojolicious-9.33 1 distribution installed Complete! Modules were installed into /path/to/carton-demo/local
のちに、Mojoliciousを利用することはやめて、Plackに変更したとしましょう。cpanfile
を書き換え、carton install
を再度実行します。
> carton install Installing modules using /path/to/carton-demo/cpanfile (省略) Successfully installed Plack-1.0050 31 distributions installed Complete! Modules were installed into /path/to/carton-demo/local
このとき、Cartonはlocal
の管理をcpanm
に丸投げしているため、Mojoliciousがcpanfile
から削除されていることを伝える手段がありません。結果として、Mojoliciousモジュールはlocal
の中に残っています。このため、carton list
などを実行すると、Mojoliciousがスナップショットに残ったままになります。
> carton list | grep Mojo Mojolicious-9.33 > grep Mojo cpanfile.snapshot Mojolicious-9.33 pathname: S/SR/SRI/Mojolicious-9.33.tar.gz (省略)
この問題を回避するためには、local
を全削除してからcarton install
をやりなおす必要があります。しかし、たびたびこうしたクリーンアップが必要になるのは手間です。また、そもそもcarton install
を再度実行すると、すべてのモジュールをはじめからインストールすることになり、依存モジュールの数が多い大きなプロジェクトでは相当な時間がかかります。
Carmelではこのような問題は起こりません。依存するモジュールはlocal
ディレクトリにインストールせず、Carmelリポジトリに保持しています。このため、
- インストールやアップデートでローカルの環境を汚すことがない
- 必要なバージョンがすでにリポジトリにある場合、インストールが高速に行われる
- アップデートやロールバックが高速かつ正確に行える
というメリットがあります。carmel install
コマンドは、すでに対象となるバージョンがCarmelリポジトリ内にある場合、そのマッピングを記録するだけですので、非常に高速に実行されます。
プロダクション環境での利用
前節で紹介したように、carmel install
コマンドは使用されるモジュールとCarmelリポジトリ内のディレクトリとのマッピングを作成し、carmel exec
コマンドで@INC
をオーバーライドすることで特定のバージョンをロードすることを実現しています。これは開発環境ではうまく動作しますし、プロダクション環境でも、Carmelリポジトリを特定のパスに指定すれば問題なく動作します。ただ、コンテナなどでの利用を前提とした場合、こうした方法よりもシンプルなやり方が用意されています。
CI環境のみでの利用
プロダクション環境ではcarmel exec
を利用したくない場合、carmel rollout
コマンドが便利です。
前述のとおり、CarmelはCartonと異なり、インストールしたモジュールを管理のためにカレントディレクトリのlocal
に保持しないようになっています。ただ、モジュールがlocal
にインストールされていると、実行時にperl -Ilocal/
とすることで、carton exec
なしにスクリプトを起動できるというメリットがありました。
これをCarmelでも実現するのがcarmel rollout
コマンドです。carmel rollout
で、Carmelリポジトリ内のモジュールをlocal
ディレクトリにエクスポートできます。
> carmel rollout Installing libwww-perl-6.70 to /path/to/demo/local Installing Encode-Locale-1.05 to /path/to/demo/local Installing File-Listing-6.15 to /path/to/demo/local Installing HTTP-Date-6.05 to /path/to/demo/local (省略)
> find local/lib local/lib local/lib/perl5 local/lib/perl5/Devel local/lib/perl5/Devel/StackTrace.pm local/lib/perl5/Devel/StackTrace local/lib/perl5/Devel/StackTrace/AsHTML.pm local/lib/perl5/Devel/StackTrace/Frame.pm local/lib/perl5/POSIX local/lib/perl5/POSIX/strftime local/lib/perl5/POSIX/strftime/Compiler.pm (省略)
cpanfile.
に指定されているバージョンを維持したまま、すべてのモジュールがlocal/
以下に展開されています。
たとえば、CI環境などでこのcarmel rollout
コマンドを実行し、local
ディレクトリ以下をコンテナのイメージに追加してしまう方法などが考えられます。筆者が運営しているサービスではこの方法でプロダクション用のコンテナイメージを作成しています。プロダクション環境carmel exec
を使う必要はなく、直接Perlのインクルードパスとして指定すれば、
> perl -I$PWD/local/lib/perl5 ./myapp.pl
のようにしてモジュールを読み込むことができます。もちろん、環境変数PERL5LIB
や、use lib
などでこのディレクトリを指定してもかまいません。
ローカル環境のみでの利用
Dockerなどのコンテナは利用せず、またプロダクション環境でCarmelをインストールできない、したくないというユースケースも考えられます。そうした場合にも、carmel package
コマンドを使うと、ローカル環境のみでCarmelを使い、CIやプロダクション環境では通常どおりモジュールをインストールできます。
carmel package
は、モジュールのアーカイブファイルやモジュールインデックスなど、CPANミラーとして利用できる一式のディレクトリをvendor/
以下に作成します。
> carmel package Copying K/KA/KAZEBURO/Apache-LogFormat-Compiler-0.36.tar.gz Copying P/PL/PLICEASE/Class-Inspector-1.36.tar.gz (省略) Writing vendor/cache/modules/02packages.details.txt.gz ---> Complete! 45 distributions are packaged in vendor/cache
このvendor/
ディレクトリをGitリポジトリに追加しておけば、Carmelがない環境cpanm
コマンドの--from
オプションにディレクトリを指定することで、cpanfile
とcpanfile.
で指定したモジュールを完全に再現できます。
> cpanm --from $PWD/vendor/cache --installdeps .
依存しているモジュールやcpanfile.
に変更があるたびに、carmel package
を実行してvendor/
以下を最新に保っていく必要があります。
まとめ
Carmelを利用することで、プロジェクトが依存しているモジュールとそのバージョンを管理できます。現在開発中のバージョンv0.
本連載