cifs でマウントした先の一部ディレクトリを mount --bind

  • Windows 上で PHP のコードを書いているプログラマの戯言ですが、非Windows、非PHP でも関係あるかもしれません
  • RHEL 系ディストリだけでしか使えないかもしれません

私は普段 Windows 上でコーティングを行い、そのコードを仮想環境の Linux で動かしています。 そのためには Windows → VM(Linux) でコードを同期する必要があるのですが、過去に幾つかの方法を試行錯誤してきました。

  1. WinSCPLinux 上のファイルをエディタで直接編集
    • ありえん
  2. Linux から Windows の共有フォルダを cifs でマウント
    • いまここ
  3. Linux の samba でエクスポートしたディレクトリを Windows でマウント
    • VM を起動しておかないとコードを見ることすらできない
    • VCS(Subversion)との相性が悪かった
      • クライアント側(TortoiseSVN)の問題だったのかも
  4. WinSCP のミラーリングで Windows から Linux にアップロード
    • 長時間 WinSCP でミラーリングしていると重い?
    • 毎度 WinSCP を立ち上げてミラーリングを設定するのがめんどい
    • たまにミスってリモートのファイルを消し飛ばす
      • たまにミスってローカルのファイルを以下略
  5. VMware Player の共有フォルダ機能を使う
    • cifs よりもファイルのI/Oが遅い気がする
    • Linux 機がローカルの VM だとは限らない
      • 社内にわりと自由に使える ESXi があるのです
  6. Eclipse FileSync で同期
    • 同期先は samba でエクスポートした Linux 上のディレクトリ
    • Eclipse で編集しないと同期されない
      • たまにエディタでちょいちょいっと編集したときにアレー?ってなる
    • VM を起動していないと Eclipse が固まる
  7. Bvckup で同期
    • 同期先は samba でエクスポートした Linux 上のディレクトリ
    • 微妙に遅延する
    • これそういう用途のものじゃない

いろいろ試した結果、初期の頃の「2. Linux から Windows の共有フォルダを cifs(samba) でマウント」に落ち着きました。

Linux から Windows の共有フォルダを cifs でマウントする方法

ググれば色々出てくるので省略。

問題点

この方法には幾つかの問題点・・・というか気になる点がありました。

ReadOnly でマウントしているのでデータやログの書き込みが出来ない

なんとなく cifs では ro でマウントしています。

rw でマウントすれば解決ですが、できるだけそれはやりたくないのです。 WinSCP でローカルのファイルを以下略した経験が活きているのです。

これはアプリの設定ファイルとかでファイルの書き込み先を変更できれば解決です。 define('LOG_DIR', __DIR__ . '/log/'); とかやると変更できないのでやめて欲しいです。

最近のプロジェクトでは必ずアプリの設定ファイルで変更できるようにしているので問題なしですが、 そうじゃなかった頃のプロジェクトも少なからずあります。

Framework や ライブラリまで cifs 経由なので動作がもっさり

最近は PHP 界隈で Composer というものが流行っていて、Framework や ライブラリもプロジェクトローカルの vendor/ ディレクトリに配置するのが主流です。

以前はこの問題を見越してプロジェクト外のディレクトリに配置するようにしていたのですが、時代の流れには逆らえないのです。

Composer で pear からインストールすると絶対パスがファイルに書き込まれることがある

PHPUnitpear からインストールしていた頃にこの問題に直面しました。 どうしようもありません。

PHPUnit ぐらいなら composer ではなく pear でシステムワイドに入れてしまってもいいかもしれません。

解決方法

これらの問題を一撃で解決する方法、それが mount --bind です。

mount --bind はマウント済のディレクトリツリーを別のツリーに再マウントする機能で、グルると色々出てきます。

mount --bindファイルシステムを超えることが出来るので、 cifs でマウントした先の一部のディレクトリを mount --bind でローカルのディレクトリに再マウントすることができます。

$ sudo mount -t cifs //ore-pc/project /home/ore/project -o (略)
$ mkdir -p /home/ore/project-l/vendor
$ sudo mount --bind /home/ore/project-l/vendor /home/ore/project/vendor
$ cd /home/ore/project
$ php composer.phar install

これで /home/ore/project は cifs でマウントされていますが /home/ore/project/vendor はローカルのディレクトリになります。

ここまで出来れば /etc/fstab で起動時に自動でマウントされるようにしたくなります。 mount --bind/etc/fstab に記述するときは、ファイルシステムを none、マウントオプションに bind を指定すればいいので・・・

//ore-pc/project            /home/ore/project        cifs   (略)
/home/ore/project-l/vendor  /home/ore/project/vendor none   bind

また、cifs のようなネットワークファイルシステムを起動時にマウントする場合、netfs サービスを有効にしておきます。

$ chkconfig netfs on

ファイルシステムのマウントはネットワークの始動よりも先に行われますが、 当然 nfs や cifs のようなネットワークファイルシステムはネットワークの始動後でなければマウント出来ません。 netfs はネットワークの始動後に nfs や cifs をマウントしてくれるようにするためのサービスです。

これで完成・・・かと思いきや、まだこれだけではうまくいきません。cifs のマウントよりも --bind のマウントの方が先に行われてしまうからです。

そのため・・・

  • 起動時の mount --bind でマウントポイントが無くて失敗する
  • シャットダウン時に cifs がアンマウントできなくてシャットダウンが停止する

といった問題が発生します。

長らくこの問題を解決する方法がわからなかったため、起動時は手動でマウント/シャットダウン時は手動でアンマウント、したり、 別の方法を模索したりしていたのですが、netfs のスクリプトを眺めていたら簡単な解決方法がわかりました。

先に述べた netfs は nfs や cifs などのファイルシステムを、起動時はネットワークの始動後にマウントしたり、 シャットダウン時にはネットワークの停止前にアンマウントしたりするものですが、 その条件(ネットワークファイルシステムと認識される条件)は、ファイルシステムnfs や cifs のようなネットワークファイルシステムであること以外に、 マウントオプションに _netdev が付いている、というものもありました。

なので /etc/fstab に次のように記述すれば大丈夫でした。

//ore-pc/project            /home/ore/project        cifs   (略)
/home/ore/project-l/vendor  /home/ore/project/vendor none   bind,_netdev

これで mount --bind もネットワーク始動後(の cifs のマウントの後)に行われるようになり、問題は解決です。