Redmineを2.0.0にアップデートした
Ruby全然分かんないんですけど割とすんなりいきました。
Redmineをアップデート
まあ普通にファイル類を置き換えるだけなんでここはとっても簡単です。
- 一応DBとかfilesディレクトリはバックアップしておけとのことなんで適当にバックアップする
mysqldump -proot --default-character-set=binary redmine > redmine.dump cp -ai files/ /tmp/
- Redmineの最新版を落としてきて適当に配置
wget http://rubyforge.org/frs/download.php/76134/redmine-2.0.0.tar.gz mv /home/redmine/ /home/redmine_old/ mv redmine-2.0.0 /home/redmine/
- 設定ファイルを配置
cp /home/redmine_old/config/database.yml config/ cp /home/redmine_old/config/configuration.yml config/ cp /home/redmine_old/config/unicorn_config.rb config/
ついでにrvmからrbenvにしてRubyを1.9.3にしちゃう
- とりあえずrvmを外す
.zshrcなり/etc/profileに入っているrvm関連のPATH設定とかを外しちゃいましょう。
- rbenv入れる
ついでにruby-buildというのを入れると「rvm install」相当のことができるらしいです。
git clone git://github.com/sstephenson/rbenv.git mv rbenv/ /usr/local/rbenv/ ; usr/local/rbenv/binをPATH設定に入れる ; eval "$(rbenv init -)"を.zshrcに入れる ; soruceも忘れずに! git clone git://github.com/sstephenson/ruby-build.git ./ruby-build/install.sh
- Rubyをインストール
rbenv install 1.9.3-p194 rbenv global 1.9.3-p194 ruby -v ; バージョン表示が正しく出ていれば成功!
- bundleする
Redmineに必要なgem一式を一気に入れてくれるそうです。
APサーバーをUnicornでやりたいのでRedmineのディレクトリに「Gemfile.local」というファイルを作り、「gem "unicorn"」を追加する。
尚、コマンドはRedmineのディレクトリで。
bundle install --without development test
- rakeコマンド
この辺まではRedmineのインストールガイドに書いてあるので普通に出来ると思います。
rake generate_secret_token rake db:migrate RAILS_ENV="production" rake tmp:cache:clear rake tmp:sessions:clear
- Unicornを起動
ログを見るとdeprecatedっぽいのがバンバン出てる気がしますが特に気にしない。
bundle exec unicorn_rails -E production -c /home/redmine/config/unicorn_config.rb -p 3000 --path /redmine
「net.ipv4.tcp_tw_recycle」を有効にするのは(場合によっては)やめた方がいい
そもそも「tcp_tw_recycle」ってなに?
TIME_WAIT状態のソケット*1を高速に再利用するためのLinuxカーネル特有の仕組みらしい。
「/etc/sysctl.conf」でこいつ(net.ipv4.tcp_tw_recycle)を1にしてやって「sysctl -p」するだけで有効になります。
「TIME_WAITのソケットを少なくしてくれるんでしょ?いいじゃん!」という感じで設定してしまいそうになりますが・・・
なんでダメなの?
結論から言うと、こいつを有効にしたとき、同じグローバルIPのクライアントからの接続でかつTCPパケットにタイムスタンプ情報が入っている場合で、ほぼ同時にパケットを送ると、古いタイムスタンプの方のパケットを勝手にドロップしちゃいます。
どういうことなの・・・
「tcp_tw_recycle」は、「同一IPからのパケットが到着したとき、使っていたソケットをすぐに開放する」というロジックの中でタイムスタンプ情報を使っているのだと思われます。
TCPパケットにタイムスタンプ情報が入っている本来の目的は、再送タイムアウトの間隔を動的に調整するためであったり、同一のTCPシーケンス番号でほぼ同時に来たパケットのうち、どっちが最新なのか(処理すべき物か)っていうのを判断するためだそうです。
そして、このタイムスタンプは端末内で付加され、Unixtimeではなく「端末の起動時間(の秒数?)」*2を送っているようです。
本来の目的であれば、前者に関してはパケット一個で整合性が取れていれば良く*3、後者に関してはTCPのシーケンス番号*4が被ったタイミングでタイムスタンプを比較すれば良いので、正常なパケットがサーバーに来た時点で破棄されるケースはほとんどないでしょう。
しかし、「tcp_tw_recycle」の仕組みでは、同一IPからくるパケットのうち、いずれかの(起動時間の短い)端末のパケットを(過去のものであるとみなして?)常に破棄するということになってしまうので大問題です。
あと、このタイムスタンプ情報は最近のOSから送られるパケットには(デフォルト設定で)大体入ってます*5。
どういうケースでまずいの?
例えば、同じWAN内のWiFiで接続しているiPhoneとAndroidとPCからほぼ同時に同じサーバーに接続したとすると、どれかひとつ*6は普通に接続出来ますが、それ以外の端末は(サーバー側で設定してある)タイムアウトまでずっと読み込み画面のままで固まってしまいます(最終的にはブラウザとかアプリ側が「接続できませんでした云々」のステータスを返します)。
特に、「同じ回線から複数の端末で接続する」ようなケースとして、テスト・デバッグ環境用のサーバーでは必ず無効にしておいた方がいいと思われます*7。
上記のようなケースが多発するのでかなりひどいです。
というか・・・
これを有効にして恩恵を受けられるケースって果たしてあるんでしょうか・・・。
昔であれば、一般的な家庭であればグローバルな回線とPCがルーターを介さずに直でつながっていたり、3G回線とかも一応それぞれにIPが割り振られるので別にいいんですが、今だったら公衆WiFiとかで違う人々が(この設定を有効にしてあるサーバーの)同じサービスとかWebページ見ようとしたら一人しかまともに接続できなくなっちゃうんですよね。
それってデメリットでかすぎじゃないですかね・・・?
そもそも、TIME_WAITを早く再利用しないといけない環境ってことは、すげーパケットのやりとりが行われていて回線がパンクしそうってことなので、素直にサーバーを増やして通信を分散させるか、どうしてもこの仕組みを利用するんであれば「net.ipv4.tcp_fin_timeout」*8をかなり短くしてやったほうが良いです。
さくらVPSで一般的な?LAMP環境を構築する
プランが一新され、今借りてるメモリ1.5Gのプランと同じ値段でCPU3コア、メモリ2Gが借りられるということで移行することにした。
その際に、イチから構築したのでその備忘録。
注意
自己流。
アカウント周り&SSH周りを変更
rootで弄り続けるのは危ない(と言われている)ので、普段使い用のアカウントを作り、sudo経由で弄れるようにする。
併せて、ssh経由でのrootログインを禁止し、パスフレーズでのsshログインも禁止する。
ユーザー追加
# useradd -g users pull # passwd pull # visudo >> 以下の部分を変更。 ## Allow root to run any commands anywhere root ALL=(ALL) ALL pull ALL=(ALL) ALL <- 追加
sshキー作成
# su - pull $ ssh-keygen -t rsa $ cd .ssh/ $ chmod 600 * $ mv id_rsa.pub authorized_keys >> 本当はこうやった方が意味合い的には良さげ? $ touch authorized_keys $ cat id_rsa.pub >> authorized_keys
id_rsaをローカルに移動に移動してわかりやすい名前に変えておく(内容をコピペでも可)。
MacおよびLinuxの場合は「chmod 600 (移動したid_rsa)」をしないと怒られるかも(WinSCPとかPuttyとかFilezilla使う場合は「puttygen」を使ってppk形式に変える必要有り)。
sshd設定
# vi /etc/ssh/sshd.conf >> 以下の部分を変更 PermitRootLogin no RSAAuthentication yes PubkeyAuthentication yes PermitEmptyPasswords no PasswordAuthentication no UsePAM no
作ったユーザーでログイン&sudo確認
ssh -i (ローカルのid_rsa) pull@(ip) sudo -s
以上が確認できたらrootでログインしているsshを切断して、以降はこのアカウントで構築作業を進める。
remi,rpmforgeリポジトリ入れる
epelはすでに入ってた・・・
# rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm # rpm -Uvh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm
enable(リポジトリのデフォルト有効化)はまあ、どっちでもいいんじゃないすかね・・・。
dagリポジトリ入れる
# vi /etc/yum.repos.d/dag.repo [dag] name=Dag baseurl=http://ftp.riken.jp/Linux/dag/redhat/el$releasever/en/$basearch/dag/ enabled=0 gpgcheck=1 gpgkey=http://dag.wieers.com/packages/RPM-GPG-KEY.dag.txt
アップデート
# yum update
途中でGPGキーの警告が出るのでy入力して進める。
ちょっと時間かかる。
Zsh入れる
この辺でいい加減「Zshといつもの.zshrcじゃねーからストレス溜まるわ!!!」ってなってくると思います。そういう方はさっさとインストールしてログインシェル変えましょう。
ちなみに、CentOS5.x系においてZshをyum経由で入れると日本語入力に難ありなのでいちいちソースから入れてたのですが、6.x系はそのバグが解消された4.3.10がインストールされるようなので*1yumで入れて問題無しです。
ついでに他の環境で使ってるオレオレ仕様の.zshrcを入れておくと良いでしょう(当たり前ですけど元の環境でしか必要ない設定は事前にコメントアウトしてからsourceした方がいいですよ)。
# yum install zsh $ chsh >> /bin/zshを指定
「sudo -s」対策(非推奨)
CentOSの5.x系と6.x系で(個人的な観点で)大きく変わっているのは「sudo -s」の挙動だと思われます。
- ホームディレクトリが変更される(ログインしたユーザーのホームではなく「/root」になる)
- ログインシェルは変わらないが、.zshrcはやはり「/root」にある.zshrcを読む
多分、これが本来の挙動なんだと思います。前者は別に良いとして、後者はなんとなく落ち着かないので「/root」ディレクトリに先ほど移動させた.zshrcのエイリアスを作成しておきます。
というよりは、「sudo -s」をやめろっていう簡単な話なんでしょうね。。。
# ln -s /home/pull/.zshrc /root/.zshrc
byobu入れる
別にscreenでもいいんですけど。
# yum --enablerepo=epel-testing install byobu
MySQL入れる
remiリポジトリから最新stable(5.5.23)を入れられたりしますが、
ソースらしきものが見当たらなかったりする*2のでrpmから入れます。
MySQLの公式サイト(多分登録が必要・・・)から以下のrpmを落として普通にインストールします。
- MySQL-5.5.23-1.el6.src.rpm
- MySQL-client-5.5.23-1.el6.x86_64.rpm
- MySQL-devel-5.5.23-1.el6.x86_64.rpm
- MySQL-server-5.5.23-1.el6.x86_64.rpm
- MySQL-shared-5.5.23-1.el6.x86_64.rpm
- MySQL-shared-compat-5.5.23-1.el6.x86_64.rpm
どうでもいいんですが、MySQL公式のダウンロードページのリスト、ディストリビューションがプルダウンで分かれてるのはいいんですが、その代わりアーキテクチャ(i686とかx64)とかrpmの種類がごっちゃになってるのでその辺もっと見やすくして欲しいです。。。
インストールする順番はserver入れてその先は適当でいいんじゃないでしょうか。
ちなみに私はserver->client->devel->shared->shared-compat->srcと入れてます。
尚、細かいですが5.x系の時とソースのインストール場所が変わってます。
(5.x系は「/usr/src/redhat/SOURCES/」に入ってたはず・・・)
とりあえず、(MySQL本体を弄らないのであれば)適当なディレクトリに移して解凍しておくといいんじゃないでしょうか。
# mv /root/rpmbuild/SOURCES/mysql-5.5.23.tar.gz /usr/local/src/ # cd /usr/local/src/ # tar xvf mysql-5.5.23.tar.gz
標準的な設定(要はmy.cnfのサンプル)が「/usr/share/mysql」(の中の頭が「my」で始まって「.cnf」でおわるもの)にあるので、適当に「my-large.cnf」あたりをコピーしておけばいいと思います。どうせ後で設定変えますよ!
cp /usr/share/mysql/my-large.cnf /etc/my.cnf
PHP入れる
remiで入れると5.3.10入るんだぜぇ〜!
でもソースからビルドして入れたいぜぇ〜!
yumで入れられるなのにだぜぇ〜!
ワイルドだろぉ〜?
とりあえず、その場合は事前に必要なライブラリ類を入れておかないとconfigureでガンガン引っかかりまくりますのでご注意下さい。
# wget http://jp2.php.net/get/php-5.3.10.tar.bz2/from/jp.php.net/mirror # tar xvf php-5.3.10.tar.bz2 # cd php-5.3.10/ # yum install zlib-devel libxml2-devel libjpeg-devel libmcrypt-devel libpng-devel openssl-devel expect curl curl-devel bzip2-devel # ./configure \ --prefix=/usr/local/php \ ; 適宜変える(この場合/usr/local/phpの中にPHPに関するファイルが全部入る) --with-mysql=/usr/bin/ \ ; この辺環境に応じて --with-mysqli=mysqlnd \ ; ネイティブドライバ --with-pdo-mysql=mysqlnd \ ; ネイティブドライバ --enable-mbstring \ --enable-zend-multibyte \ --with-libdir=lib64 \ --with-gd \ --with-jpeg-dir=/usr/lib \ --with-png-dir=/usr/lib \ --with-apxs2 \ --with-mcrypt \ --with-openssl \ --with-zlib \ --with-bz2 \ --enable-zip \ --with-curl \ --enable-pcntl # make
makeに割と時間が掛かります。
前にさくらVPSでビルドしたときはするするっと行った気がするんですが、今回はmakeの途中で怒られました。
/usr/bin/ld: cannot find -lltdl collect2: ld returned 1 exit status make: *** [libphp5.la] エラー 1
なんかlibmcrypt関連のエラーらしいです。
ここのエラー14を参考にして必要なのをインストールしてからmakeすると今度はちゃんと成功しました。
ちなみに(上記設定だと)設定ファイルの場所は「/usr/local/php/lib/php.ini」になります。
とりあえず標準設定をコピーしておきましょう。
# cp /usr/local/src/php-5.3.10/php.ini-production /usr/local/php/lib/php.ini
設定ファイルの場所はcofigure時のオプション「--with-config-file-path=」で変えられるので、たとえば「/etc/php.ini」にしたい場合は「--with-config-file-path=/etc」とすると良いと思います。
PHPのbinディレクトリをPATHに入れる
上記のように、ソースから入れてprefixを変えた場合、php関係のバイナリが全部prefixで指定したディレクトリ以下のbinディレクトリに入ると思うので、.zshrcなり/etc/profileなりにPATHの設定を追加しておかないとcli環境が使えません(たぶんmod_phpは使える)。
$ vi ~/.zshrc >> 以下を追加 export PATH=${PATH}:/usr/local/php/bin
(pecl installで入れた)エクステンションがインストールされる場所
試しにapcなんかをpeclコマンドで入れると、後ろのほうにインストールしたディレクトリが出てくるはずです。恐らくCentOSであれば以下のような場所にインストールされるはず。。。
# pecl install apc # ll /usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/ 合計 728 -rw-r--r-- 1 root root 741397 4月 20 17:32 2012 apc.so
php.ini変更
# vi /usr/local/php/lib/php.ini >> 以下の部分を弄っておけばとりあえずなんとか動くはずです > 変更 short_open_tag = Off(or On) extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20090626" max_execution_time = (適宜) memory_limit = (適宜) date.timezone = Asia/Tokyo session.save_path = "/var/lib/php/session" mysql.default_socket= /var/lib/mysql/mysql.sock mysqli.default_socket= /var/lib/mysql/mysql.sock pdo_mysql.default_socket= /var/lib/mysql/mysql.sock > 追加 [apc] extension=apc.so
上記にある通り、session.save_pathを変更した場合、当然そのディレクトリを作成してApacheで読み書きできるようにしておかないとセッション管理が出来ませんのでご注意を。
PHPがちゃんとインストールされてるか確認
# php -v PHP 5.3.10 (cli) (built: Apr 20 2012 17:24:33) Copyright (c) 1997-2012 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies # php -m [PHP Modules] apc <- あるか確認 bz2 Core ctype curl date dom ereg fileinfo filter gd hash iconv json libxml mbstring mcrypt mysql mysqli mysqlnd openssl pcntl pcre PDO pdo_mysql pdo_sqlite Phar posix Reflection session SimpleXML SPL SQLite sqlite3 standard tokenizer xml xmlreader xmlwriter zip zlib [Zend Modules]
続・間違いだらけの負荷対策
事の始まり
3月に入ってから「斉藤さん」のWebサーバーのピークタイムでのアクセス数が秒間180アクセスを境に動かなくなり、しまいには一部アクセスが固まって動かなくなるという現象が発生した。
Apacheのエラーログを見ると、データベースサーバーに接続できていない旨が出ていたため、その辺を念頭に置いていろいろと探ってみたのだが・・・。
ソケット数の限界?
「netstat -ant」と打つと、その時点でのTCPコネクションの一覧が出る。
ピーク時にWebサーバー側は30000前後、DBサーバー側は8000前後。
HandlerSocketのポート(標準では9998と9999)に絞ってみてみると、Webサーバー側は8000前後なのに対し、DBサーバー側は500前後。
明らかにWebサーバーのコネクション数がおかしい。
MySQLのコネクション数は問題なさそうなので、HandlerSocketのソケットが開けなくてパケットがドロップしてる?と想像して、/etc/sysctl.confにいろいろ設定を書いてみるが駄目。
iptables?
そういえばカーネル周りの設定をしておきながらカーネル周りのログを見ていなかった、と思い/var/log/messageを見てみると以下のようなログが出ていた。
Mar 8 02:57:26 api kernel: ip_conntrack: table full, dropping packet. Mar 8 02:57:38 api kernel: printk: 346 messages suppressed.
これを元にググってみると、どうやらiptablesに関係するエラーらしいということが分かった。
ちなみに、ニフティクラウドのサーバーは建てた時点で外部ネットワークに晒されるようになっているため、iptablesによるポートフィルタリングがデフォルトで設定された状態*1である。
この、iptablesはパケットを解析する際に/proc/net/ip_conntrackというファイルにその情報を記録するのだが、テーブルの上限数が設定されており、それを超えてしまうとパケットを落としてしまうらしい。
現在のiptablesのテーブルの項目数(?)とその限界は以下のように確認できる。
# 最大 $ cat /proc/sys/net/ipv4/ip_conntrack_max 65536 # 使ってる項目数 $ cat /proc/sys/net/ipv4/netfilter/ip_conntrack_count 63642
うおおおおテラギリギリスwwwwwwww
というわけで、そいつを上げればいいんだなと思い、両方のサーバーの/etc/sysctl.confに
net.ipv4.ip_conntrack_max = 131072
を設定してApacheBenchを動かしてみると、今まで途中で止まっていたベンチがスムーズに動くようになった。
まとめ
参考にしたサイト
中〜大規模サーバーを運用するときの勘所 – iptablesとip_conntrack – cyano
http://www.sssg.org/blogs/naoya/archives/1454
もしかして・・・
(アクセス裁けなかったの)Nginxのせいじゃない・・・!?
Nginxで2週間ほど『斉藤さん』のサーバーサイドを運用してみて
考察
- 同時アクセス数が大きくなると(感覚では秒間150〜160前後)後ろが詰まってアクセスを取りこぼすことがある。下のグラフの0時前後(23時ちょうどと0時過ぎくらい)に注目。グラフが途切れている部分はアクセスが集中し、munin-nodeがステータスページへアクセスしても結果がうまくとってこれなくなっている(という個人的な解釈ですが・・・)。
結論
結論としては「動的な処理にはこの組み合わせはあんまり向いてないんじゃないか???」ということになりました。
というわけで、結局WebサーバーをApacheに戻しました。
ただ、静的なもの(HTMLとか画像ファイル)は適切なキャッシュの設定をすれば爆速ですし、動的なものでも秒間100アクセスレベルくらいならWebサーバー部分のメモリ消費を抑えて、その代わりに(同じサーバーに入れている)MySQL等のメモリ消費量を増やす、という策が取れそうでいいかもしれません。
何だかんだ言って、適材適所ですね。
おまけ
PHP-FPMはこんな感じで推移してました。
『斉藤さん』のウィークリーでのアクセス数のグラフ。
他のサービス同様、平日・祝日共に「18時前後」「0時前後」に大きな山ができています。学生さんの昼休みの時間帯と思われる「(平日)12時〜13時」のあたりにも小さな山ができているのが特徴的です。
お手軽メッセージキュー「Kestrel」を使ってみる
いきさつ
ついに『斉藤さん』が北米・UKなどで公開されたらしいです。
海外ではウケるのかどうか未知数ではありますが、さらにアクセス数が増えると思うとgkbrな日々です。
今のところWebサーバーのプロセスが比較的スムーズに処理されているようなので問題ないのですが、「ある程度時間のかかる処理で、その処理の結果を返す必要がないもの*1」の割合が多くなってきた場合、処理の待ち時間が長くなってしまい、Webサーバーでのプロセスが溜まってしまう、というようなことが起きてしまう懸念点があります。
そういう処理ってWebサーバー側ではやることやったらレスポンスを返して、時間のかかる処理自体はバックエンドでやりたいですよね?
まあ、いま流行りの「非同期処理」というやつですね。
非同期処理をする場合はいわゆる「この処理をこういう条件でやってね!」→「はーい!わかりましたー!」というメッセージングのやり取りが必要になるので、Memcachedプロトコルを話せるお手軽なメッセージキューサーバー「Kestrel」を導入することにしました。
インストール
KestrelはScalaで作られているそうなので、Javaが動く環境なら公式サイトからビルド済みのプログラムファイル一式を持ってきて、適当な場所に置いてしまえば、あとは普通にjarファイルを実行するだけで使えます。
そして、常駐させておきたいのでデーモン化するためにシェルスクリプトをごりごり書く・・・のでもいいのですが、最近は常駐プログラムを管理するときに「Supervisor」というプロセス管理ツールを使うのが流行りのようなので、そっちでKestrelを常駐させるように設定します*2。
ついでに、タスクワーカー(タスクを処理するプログラム)もSupervisorで常駐させてしまうといい感じです。
詳しくはここを見ればだいたいわかります。
ハマった点
PHPのMemcached拡張(pecl-memcached)は、セットしたいデータが100バイトを超えると自動的に圧縮をかけるらしい。
普通にMemcache使う場合は自動的に解凍してくれるので問題ないが、Kestrelでは解凍してくれないので圧縮を無効化する必要がある。
その他
配列とかを入れても自動的にシリアライズ・デシリアライズしてくれますが、Msgpackとかが使える環境であれば、それをつかって手動でシリアライズ・デシリアライズしたほうが良さげ(多分そっちのほうが速い)。
サンプル(タスクキュー送信)
<?php $mem = new Memcached; $mem->addServer(KESTREL_HOST, 22133); // セットする際に自動的に圧縮しないようにする $mem->setOption(Memcached::OPT_COMPRESSION, false); $data = array('key'=>'value'); $data = msgpack_pack($data); // 「message」キューのタスクをセット($dataの中には処理に必要なデータを入れる) $mem->set('message', $data);
サンプル(ワーカープロセス)
<?php $mem = new Memcached; $mem->addServer(KESTREL_HOST, 22133); for (;;) { // 「message」キューのタスクがあるか2秒待つ $result = $mem->get('message/t=2000/open'); if ($result !== false) { $result = msgpack_unpack($result); // ここで$resultに応じて処理をする // 下記はAndroidのPush通知を送る処理の例 $registrationId = $result['registration_id']; $collapseKey = $result['collapse_key']; $data = $result['data']; $header = array(); $header[] = "Content-Type: application/x-www-form-urlencoded"; $header[] = "Authorization: GoogleLogin auth=" . GOOGLE_C2DM_TOKEN; $post = array(); $post['registration_id'] = $registrationId; $post['collapse_key'] = urlencode($collapseKey); foreach ($data as $name => $d) { $post['data.' . $name] = urlencode($d); } $postf = http_build_query($post, '&'); $ch = curl_init(GOOGLE_C2DM_URL); $options = array( CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_FAILONERROR => TRUE, CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_POST => TRUE, CURLOPT_HTTPHEADER => $header, CURLOPT_POSTFIELDS => $postf, CURLOPT_HEADER => TRUE, CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 15, ); curl_setopt_array($ch, $options); curl_exec($ch); curl_close($ch); $result = $mem->get('message/close'); } }
ディスクの中にあるディレクトリをサイズ順に並べるPHPスクリプト
セーフモードが有効だと多分動きません。
<?php exec('du -Sh /', $return); foreach ($return as $data) { $data2 = explode("\t", $data); $size = $data2[0]; $unit = substr($size, -1); $size = substr($size, 0, -1); switch ($unit) { case 'G': $size2 = $size * 1000 * 1000; break; case 'M': $size2 = $size * 1000; break; default: $size2 = $size; break; } $sizeList[] = array($size2, $data2[0], $data2[1]); } usort($sizeList, 'sort_for_number'); var_dump($sizeList); function sort_for_number($a, $b) { return ($a[0] < $b[0]) ? TRUE : FALSE; }
こんな感じで表示される。膨大な量が出てくるはずなのでmoreとかlessとかを繋げといたほうが良さげ。
array(25543) { [0]=> array(3) { [0]=> float(5300000) [1]=> string(4) "5.3G" [2]=> string(14) "/var/lib/mysql" } [1]=> array(3) { [0]=> float(1700000) [1]=> string(4) "1.7G" [2]=> string(9) "/home/xxx" } [2]=> array(3) { [0]=> float(1100000) [1]=> string(4) "1.1G" [2]=> string(41) "/home/www/xxx.xxx.com/logs/20120214" } [3]=> array(3) { [0]=> int(678000) [1]=> string(4) "678M" [2]=> string(32) "/home/www/xxxx.xxx.com/logs" } [4]=> array(3) { [0]=> int(417000) [1]=> string(4) "417M" [2]=> string(34) "/home/www/xxxxx.xxx.com/logs" } ...(以下略)