Linux で作業していると、ディレクトリを作ってから所有者と権限を直す、という手順を何度も踏むことになります。
$ mkdir /var/www/app
$ chown appuser:appuser /var/www/app
$ chmod 755 /var/www/app
3回に分けるのが地味に面倒です。install -d を使うと、これを1回で済ませられます。
$ install -d -m 0755 -o appuser -g appuser /var/www/app
なぜ面倒なのか
Web アプリケーションを /var/www/app に置く、といった配置をよくします。このとき所有者は一段ごとに違うことが多いはずです。
/var/wwwはrootのまま/var/www/appはアプリケーションの実行ユーザー(nginxや専用ユーザー)
この「親は root、その下だけ別ユーザー」という形を作るのが、mkdir と chown の組み合わせだと少し気を使います。mkdir -p で一気に作ってから chown -R をかけると、変えたくない親まで巻き込むからです。
install -d は最終ディレクトリだけに適用される
install -d の便利なところは、途中のディレクトリは既定のまま作り、指定した所有者と権限は最後のディレクトリにだけ適用する点です。
$ install -d -m 0755 -o appuser -g appuser /var/www/app
結果を確認すると、こうなります。
| パス | 所有者 | 権限 |
|---|---|---|
/var | root:root | 755 |
/var/www | root:root | 755 |
/var/www/app | appuser:appuser | 755 |
まさに冒頭で挙げた「親は root、その下だけアプリユーザー」がそのまま出来上がります。mkdir -p 相当の働きをしつつ、所有者の適用範囲は最後だけ、というのがちょうどよく効きます。
オプションはそれぞれ次の意味です。
-d— ディレクトリを作成する(親が無ければまとめて作る)-m— 権限(chmod相当)-o— 所有者(chown相当)-g— グループ
chown -R だと親まで巻き込む
比較のために、mkdir -p してから chown -R をかけた場合を見てみます。
$ mkdir -p /var/www/app
$ chown -R appuser:appuser /var/www
| パス | 所有者 | 権限 |
|---|---|---|
/var/www | appuser:appuser | 755 |
/var/www/app | appuser:appuser | 755 |
/var/www の所有者まで変わってしまいました。-R を付けた以上は当然の結果ですが、ここを root のままにしておきたい場面は多いはずです。かといって chown を階層ごとに打ち分けるのも面倒で、結局そこが install -d の出番になります。
既存のディレクトリにも使える
install -d は、対象がすでに存在していてもエラーにならず、所有者と権限を指定どおりに直してくれます。
# 実行前: root:root 700
$ install -d -m 0755 -o appuser -g appuser /srv/app
# 実行後: appuser:appuser 755
終了コードは 0 です。「無ければ作る、あれば整える」という動きなので、セットアップ用のスクリプトに置いておくと何度流しても同じ状態に落ち着きます。Ansible の file モジュールを手で書いているような感覚に近いです。
一方で、既存ディレクトリの中身には影響しません。
$ install -d -m 0755 /srv/app
これを実行しても、/srv/app の中にあるファイルやサブディレクトリの権限はそのままです。chmod -R のつもりで使うと目的を果たせないので、そこは区別してください。逆に言えば、うっかり中身を書き換えてしまう心配もありません。
umask の影響を受けない
-m を省略した場合、install -d は umask に関係なく 755 で作成します。mkdir と挙動が違う点です。
| umask | install -d | mkdir |
|---|---|---|
| 022 | 755 | 755 |
| 077 | 755 | 700 |
| 000 | 755 | 777 |
umask を絞った環境で mkdir と同じつもりで使うと、意図より緩い権限になります。権限を明示したいなら -m は常に書くのが安全です。
グループで共有したいときは setgid
アプリユーザーが書き込み、nginx が読む、といった共有をしたい場合は setgid(2 始まりのモード)を付けておくと、あとから中に作られるファイルがディレクトリのグループを引き継ぎます。
$ install -d -m 2775 -o appuser -g www-data /var/www/app
このディレクトリの中にファイルを作ると、作成者が誰であってもグループは www-data になります。デプロイのたびにグループを直して回る手間が減ります。
Dockerfile や Ansible での書き方
Dockerfile では RUN に一行書くだけで、レイヤーを増やさずに済みます。
RUN install -d -m 0755 -o appuser -g appuser /var/www/app
指定するユーザーとグループがその時点で存在している必要があるので、useradd の後に置いてください。なお BusyBox の install でも同じ挙動なので、Alpine ベースのイメージでもそのまま使えます。
Ansible を使っているなら、素直に file モジュールを使うほうが読みやすいです。
- name: アプリケーションのディレクトリを作成する
ansible.builtin.file:
path: /var/www/app
state: directory
owner: appuser
group: appuser
mode: "0755"
install -d の出番は、シェルスクリプトや Dockerfile、あるいは手作業で ssh に入っているときです。
まとめ
install -d -m MODE -o USER -g GROUP PATHで、作成・所有者・権限を1回で指定できる- 途中のディレクトリは既定のまま作られるので、
/var/wwwは root、/var/www/appだけアプリユーザー、という形がそのまま作れる - 既存ディレクトリにも使えるが、中身には影響しない
-mを省略すると umask に関係なく 755 になる
地味に便利です。