Perl版のHerokuを作りたい
発端
最近Salesforceに買われたのでちまたでも有名になったHeroku,RubyOnRailsをオンデマンドで使う事ができるサービスです.このサービスではgitで管理しているソースをHeroku環境にデプロイしてmakeするとwebアプリケーションを実行できます.似たようなことをPerlでもやってみたいなと思っていました.
一方で子飼さんが作ったlleval - run codes from your browserというサービスと,これを元にした宮川さんのhttp://sunaba.plackperl.org/app/jrl_h69c3xgmlha_byudvgというサービス(というかproof-of-conceptかな)が動いています.これらの巨人(子飼さんはともかく宮川さんはでかくないけど)の偉業を足がかりにすればできるんじゃないかと思いつきました.
そして,最近iTunes App StoreでCodeToGoというアプリを見つけて,もしかしたらニーズもあるかもしれないと思うようになりました.ビジネスモデルは(苦手だから)後回しにして,とりあえず動きそうなアーキテクチャを考えてみることにしました.
構想
アーキテクチャに名前がないと説明しにくいのでHakoniwa(箱庭)という名前を付けてみました.まず,ローカル環境に必要なアプリケーションをインストールして,
$ cpanm App::Hakoniwa
例えば,bonsan(盆山)ってwebアプリを作る場合には
$ hakoniwa create bonsan
とします.するとHakoniwaがaws(amazon web service)上にbonsan.hakoniwa.com, bonsan.api.hakoniwa.comという名前のサーバ(AMIインスタンス)用意します.bonsan.hakoniwa.comにはsunaba(もしくはその拡張)が,bonsan.api.hakoniwa.comにはlleval(の移植版)が動きます.
次にデプロイするにはgitを使います.Hakoniwaが必要な環境変数(の初期値を)用意しておけばgitがそのまま使えるかなと.
$ git push hakoniwa master
アプリケーションを起動はこれでいいかな.
$ hakoniwa plackup bonsan
負荷分散は要求に応じてbonsan001, bonsan002...とアプリケーションサーバを増やして,それをsunabaに組み込んだproxy機能で分散させればいいと思っています.
hakoniwaのコマンドオプションはこれぐらいあればいいかと.
$ hakoniwa --help help # show this usage version # show hakoniwa version list # list your apps create <name> # create a new app destroy <name> # destroy the app permanently info [<name>] # show app info, like web url and git rep api:add [<num>] # add api server api:del [<num>] # delete api server keys # show your user's public keys keys:add [<path to keyfile>] # add a public key keys:remove <keyname> # remove a key by keyname keys:clear # remove all keys domains:add <domain> # add a custom domain name domains:remove <domain> # remove a custom domain name domains:clear # remove all custom domains ssl:add <pem> <key> # add SSL cert to the app ssl:remove <domain> # removes SSL cert from the app domain pm # list installed modules pm:install <perl_module> # install the module from CPAN pm:uninstall <perl_nmodule> # remove mod pm:update <perl_module> # update specified module
apiサーバが複数あるからsshして作業するわけにはいかないだろうから,capistranoも使えるようにする必要があるかもしれないなぁ.とまぁ,こんな記事を書いておいたら誰か作ってくれないかな,と淡い期待.それともこんな仕組みには誰も興味ないでしょうか.
micro typeでの準備
perl5 on AMI Linux t1.micro
awsを使ってapi.hakoniwa.com用の環境を作ってみようと思いました.試しに作ってみたときのメモです.用意したインスタンスは,
AMI: Amazon Linux AMI ID ami-2272864b (x86_64)
Name: Basic 64-bit Amazon Linux AMI 1.0
$ ssh -i .ssh/awskey -o ServerAliveInterval=5 ec2-user@ec2- ... amazonaws.com
後は普段通りにperl5をインストールします.
$ sudo yum update $ sum yum groupinstall 'Development Tools' $ sudo yum install git $ curl -LO http://xrl.us/perlbrew $ chmod +x perlbrew $ ./perlbrew install $ /home/ec2-user/perl5/perlbrew/bin/perlbrew init
ここまでできたら,~/.bashrcに以下の行を追加します.
source /home/ec2-user/perl5/perlbrew/etc/bashrc
さらに作業を続けます.
$ . .bashrc $ perlbrew install perl-5.12.2 $ perlbrew switch perl-5.12.2 $ curl -L http://cpanmin.us/ | perl - App::cpanminus $ cpanm App::cpanoutdated $ cpanm App::pmuninstall $ cpan-outdated | cpanm
これでperl5がインストールできました.
perl6 on AMI Linux t1.micro
使うつっもりはまだないですが,JPerl Advent Calendar 2010 Perl6 Tracを読んでperl6にも興味を持ちました.perl5をインストールしたついでにperl6のインストール方法もまとめてみました.AMIは上と同じようにt1.microを使っています.
$ sudo yum update $ sudo yum groupinstall 'Development Tools' $ sudo yum install git $ sudo yum install libicu-devel
m1.typeの環境ではperl6作成の時にメモリがたりず,oom killerが発動します.そのためswap領域を追加しておきます.
$ sudo dd if=/dev/zero of=/myswap0 bs=1M count=2048 $ sudo mkswap /myswap0 $ sudo swapon /myswap0
これで,/proc/swapsを見ると以下のようになっているはずです.
Filename Type Size Used Priority /myswap0 file 2097148 0 -1
次にbootした時にもswapを使えるようにするためには/etc/fstabに以下の行を追加しておきます.
/myswap0 swap swap defaults 0 0
これで準備ができました.AMI Linuxにはperl5が入っているので,そのまま使えば…と思ったのですが,perl6を作る際にエラーが出たので上と同じようにインストールしました.
$ curl -LO http://xrl.us/perlbrew $ chmod +x perlbrew $ ./perlbrew install $ /home/ec2-user/perl5/perlbrew/bin/perlbrew init ## Add next line to the ~/.bashrc source /home/ec2-user/perl5/perlbrew/etc/bashrc $ . .bashrc $ perlbrew install perl-5.12.2 $ perlbrew switch perl-5.12.2
さぁこれでperl6を作る準備ができました.
$ git clone git://github.com/rakudo/rakudo.git
$ cd rakudo
$ perl Configure.pl--gen-parrot
$ make
$ make install
Configure.plには15分ぐらい,makeには7時間ぐらいかかります.これでできるperl6はrakudo環境のみでrakudo starディストリビューションに入っているモジュールが入っていません(ファイルははいってるんだけど,ライブラリパスとは違う場所に入っていたり,そもそもなかったり).そのため公開されているモジュールを使ってみよう - JPerl Advent Calendar 2010 Perl6 Trackで紹介されているneutroコマンドを使って必要なモジュールをインストールする必要があります.
といっても,モジュールがないことに気づいてrakudo starに切り替えてそのあとにneutroのことを知ったので,実際に試していません.もし違っていたら,ここの文章を書き直すようにします.
chroot環境を作る
perlも作れたし,前回のメモでforkを禁止するモジュールも作ったし,後やらないといけないのは/chroot環境を作ればllevalを移植できるはず.ということでchroot環境を作ってperlが動くところまで試してみました.
$ sudo mkdir /chroot $ sudo mkdir /chroot/bin $ sudo mkdir /chroot/lib64 $ sudo mkdir /chroot/usr $ sudo mkdir /chroot/tmp $ sudo mkdir /chroot/dev $ sudo mkdir /chroot/etc $ sudo mkdir /chroot/home $ sudo mknod -m 666 /chroot/dev/null c 1 3 $ sudo mknod -m 666 /chroot/dev/zero c 1 5 $ sudo mknod -m 666 /chroot/dev/random c 1 8
と,これで必要最低限必要なディレクトリとファイルを作りました.まずはbashとlsが動くようにライブラリをコピーしましょう.ライブラリはlddで探すことができます.
$ ldd /bin/ls: linux-vdso.so.1 => (0x00007fff1698b000) libselinux.so.1 => /lib64/libselinux.so.1 (0x0000003fc4a00000) librt.so.1 => /lib64/librt.so.1 (0x0000003fc7200000) libcap.so.2 => /lib64/libcap.so.2 (0x0000003fca200000) libacl.so.1 => /lib64/libacl.so.1 (0x0000003fc7600000) libc.so.6 => /lib64/libc.so.6 (0x0000003fc4200000) libdl.so.2 => /lib64/libdl.so.2 (0x0000003fc4600000) /lib64/ld-linux-x86-64.so.2 (0x0000003fc3e00000) libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003fc4e00000) libattr.so.1 => /lib64/libattr.so.1 (0x0000003fc6e00000) $ ldd /bin/bash linux-vdso.so.1 => (0x00007fffeecaf000) libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003fc6600000) libdl.so.2 => /lib64/libdl.so.2 (0x0000003fc4600000) libc.so.6 => /lib64/libc.so.6 (0x0000003fc4200000) /lib64/ld-linux-x86-64.so.2 (0x0000003fc3e00000)
ここに出て来た/lib64配下のライブラリを/chroot/lib64にコピーします.んで,
sudo chroot --userspec=ec2-user /chroot
とすることでchroot環境に移ります.
$ sudo chroot --userspec=ec2-user /chroot bash-4.1$ ls bin dev etc home lib lib64 tmp usr bash-4.1$ cd bash-4.1$ pwd /home/ec2-user bash-4.1$ exit exit
ちなみに,色々入れた後に確認した結果を書いてるので,手順通りに作った場合とは表示が違うかもしれません.次にbashやlsと同じようにperlでもライブラリを調べて
$ ldd `which perl` linux-vdso.so.1 => (0x00007fff9b5ff000) libnsl.so.1 => /lib64/libnsl.so.1 (0x0000003fc8e00000) libdl.so.2 => /lib64/libdl.so.2 (0x0000003fc4600000) libm.so.6 => /lib64/libm.so.6 (0x0000003fc5600000) libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003fc5a00000) libutil.so.1 => /lib64/libutil.so.1 (0x0000003fc9200000) libc.so.6 => /lib64/libc.so.6 (0x0000003fc4200000) /lib64/ld-linux-x86-64.so.2 (0x0000003fc3e00000) libfreebl3.so => /lib64/libfreebl3.so (0x0000003fc5e00000)
コマンドをいくつか実行してみて動かなかったらstraceを使って足りないライブラリを見つけては足して行くという作業を続けました.そして今の環境はこんな感じ.
$ tree --filelimit 18 /chroot /chroot ├── bin │ ├── bash │ ├── curl │ ├── ls │ └── ping ├── dev │ ├── null │ ├── random │ └── zero ├── etc │ ├── group │ ├── host.conf │ ├── hosts │ ├── localtime │ ├── nsswitch.conf │ ├── passwd │ └── resolv.conf ├── home │ └── ec2-user │ └── perl5 ... ├── lib64 │ ├── ld-linux-x86-64.so.2 │ ├── libacl.so.1 │ ├── libattr.so.1 │ ├── libcap.so.2 │ ├── libcrypt.so.1 │ ├── libc.so.6 │ ├── libdl.so.2 │ ├── libfreebl3.so │ ├── libm.so.6 │ ├── libnsl.so.1 │ ├── libnss_dns.so.2 │ ├── libnss_files.so.2 │ ├── libpthread.so.0 │ ├── libresolv.so.2 │ ├── librt.so.1 │ ├── libselinux.so.1 │ ├── libtinfo.so.5 │ └── libutil.so.1 ├── tmp └── usr ├── bin │ ├── id │ └── strace ├── lib │ └── locale │ └── locale-archive └── lib64 ├── libcurl.so.4 └── libidn.so.11 29 directories, 44 files
$ sudo chroot --userspec=ec2-user /chroot bash-4.1$ export PATH=/home/ec2-user/perl5/perlbrew/perls/perl-5.12.2/bin/:$PATH bash-4.1$ perl -v This is perl 5, version 12, subversion 2 (v5.12.2) built for x86_64-linux Copyright 1987-2010, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using "man perl" or "perldoc perl". If you have access to the Internet, point your browser at http://www.perl.org/, the Perl Home Page.
ってことで,llevalでの特徴となっているLWP::Simpleが動くかどうかを試してみると,
bash-4.1$ perl -MLWP::Simple -le 'getprint "http://www.perl.com"' <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" id="sixapart-standard"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="generator" content="Movable Type Pro 5.02" /> <link rel="stylesheet" href="http://www.perl.com/pub/styles.css" type="text/css" /> <link rel="start" href="http://www.perl.com/pub/" title="Home" /> ...
となるので,成功です.このとき,/etc/passwdは
ec2-user:x:222:500:EC2 Default User:/home/ec2-user:/bin/bash
同様に/etc/groupは
ec2-user:x:500:
としています.
ちなみに,今気になっているのはidコマンドの結果です.普通は
$ id uid=222(ec2-user) gid=500(ec2-user) groups=500(ec2-user),10(wheel)
となるはずですが,chroot環境では
bash-4.1$ id uid=222(ec2-user) gid=0 groups=500(ec2-user),0,1,2,3,4,6,10
となっています.なぜに0とかがでてくるのかよくわかっていません.大丈夫なのかな?