Capistrano3でSymfony2をデプロイしたら、app/cache配下に書き込みできなくて数時間ハマった


## はじめに
`[UNMAINTAINED]` になった `Capistrano2` + `Capifony`プラグインから、`Capistrano3` + `capistrano/symfony`プラグインに切り替えたら、キャッシュ周りで数時間ハマりました。

## 何が起きたか
`Capistrano3` + `capistrano/symfony`プラグインに切り替えて、デプロイしたところ、直後のアクセスで`Apache`が下記のようなエラーを吐きました。

```
request.CRITICAL: Exception thrown when handling an exception (InvalidArgumentException: The directory "{/path/to/app}/releases/{timestamp}/app/cache/prod/jms_serializer" is not writable. at {/path/to/app}/releases/{timestamp}/vendor/jms/metadata/src/Metadata/Cache/FileCache.php line 17) {"exception":"[object] (InvalidArgumentException(code: 0): The directory \"{/path/to/app}/releases/{timestamp}/app/cache/prod/jms_serializer\" is not writable. at {/path/to/app}/releases/{timestamp}/vendor/jms/metadata/src/Metadata/Cache/FileCache.php:17)"} []
```

前置きしておきますが、`jms_serializer`は無関係です。
他のライブラリも同様に、`app/cache`に書き込みできなくて、エラーを吐きます。

## `app/cache` は `0777` に設定しているはず
`config/deploy.rb`には、下記のように、`app/cache`を`0777`にしているつもりでした。

```
# config/deploy.rb

# Set correct permissions between releases, this is turned off by default
set :file_permissions_paths, ["app/cache"]
set :file_permissions_users, ["apache"]
set :file_permissions_chmod_mode, "0777"
set :permission_method, :acl
after "deploy:updated", "deploy:set_permissions:acl"
```

## Symfony的には、ACLを使うべし
ファイルのパーミッションについて、Symfonyの公式ドキュメントに詳しい説明があります。

- [Setting up or Fixing File Permissions (current)](http://symfony.com/doc/current/setup/file_permissions.html#using-acl-on-a-system-that-supports-setfacl-linux-bsd)

```bash
$ HTTPDUSER=$(ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\  -f1)
# # if this doesn't work, try adding `-n` option
$ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var
$ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var
```

`setfacl`コマンドによって、Webサーバの実行ユーザー（apache, www-dataなど）とパーミッションを設定するユーザー（$(whoami)）双方にフルアクセスを与えるコマンド例が記載されています。

## `setfacl` は実行されている
`Capistrano::FilePermissions`の[README](https://github.com/capistrano/file-permissions#acl)を見ると、下記のようにコマンドを実行していることが説明されています。

```
[..] setfacl -Rn -m u:www-data:rwX -m u:<deploy-user>:rwX <path-to-app>/shared/app/logs <path-to-app>/<release>/app/cache
```

実際、デプロイ時のコンソールログを見ると、下記のように`setfacl`コマンドが実行されています。

```
00:06 deploy:set_permissions:acl
      01 setfacl -Rn -m u:{web server user}:rwX -m u:{deploy user}:rwX {/path/to/app}/releases/{timestamp}/app/cache
    ✔ 01 {deploy user}@{web server} 0.047s
      02 setfacl -dRn -m u:{web server user}:rwX -m u:{deploy user}:rwX {/path/to/app}/releases/{timestamp}/app/cache
    ✔ 02 {deploy user}@{web server} 0.047s
```

## しかし、パーミッションは`0777`になっていない
確認してみると、パーミッションは`0777`になっていないことに気付きます。

```bash
[{deploy user}@{web server} {/path/to/user}]$ ls -la current/app/cache

drwxr-xr-x+ 3 {deploy user} {group} 4096  5 25 19:42 2017 .
drwxr-xr-x  5 {deploy user} {group} 4096  5 25 19:42 2017 ..
drwxr-xr-x+ 9 {deploy user} {group} 4096  5 25 19:42 2017 prod
```

```bash
[{deploy user}@{web server} {/path/to/user}]$ getfacl current/app/cache/*
# file: current/app/cache/prod
# owner: {deploy user}
# group: {group}
user::rwx
group::r-x
other::r-x
```

## 既知の問題ではある
おかしいですね。だけど、これはみんな踏んでいる地雷なのでは？ということで、Githubを見ていると、下記のPRを発見しました。

- [Do not use -n flag with setfacl #9 capistrano/file-permissions](https://github.com/capistrano/file-permissions/pull/9)

![remove_n_option.png](https://qiita-image-store.s3.amazonaws.com/0/26422/0a8ab13b-c8b0-adf5-6e51-afb9803a8564.png "remove_n_option.png")

詳しいことは理解しきってはいないですが、どうやら、`setfacl`の`n`オプションが不要だったみたいです。
あれ、これ、`Merged`になっているけれども...

## 修正はされているけれど、リリースされていない
そう、このPRがマージされたのは、`2016-6-29`で、最新の`capistrano-file-permissions 1.0.0`は、`2016-1-15`のままなんですよね。
これに気付くのに、もう小一時間かかりました。

- [capistrano-file-permissions 1.0.0](https://rubygems.org/gems/capistrano-file-permissions/versions/1.0.0)

で、早くリリースしてくれっていうissueが`2017-5-3`にすでに切られてました。

- [Ask for a new release with setfacl #15](https://github.com/capistrano/file-permissions/issues/15)

## `umask` を使うことに
ACLによるファイルパーミッションをあきらめるとなると、`umask`を使う方法がSymfonyの公式で紹介されています。

- [Setting up or Fixing File Permissions (current) #4. Without Using ACL](http://symfony.com/doc/current/setup/file_permissions.html#without-using-acl)

```
If none of the previous methods work for you, change the umask so that the cache and log directories are group-writable or world-writable (depending if the web server user and the command line user are in the same group or not). 
To achieve this, put the following line at the beginning of the bin/console, web/app.php and web/app_dev.php files:
```

`bin/console`（symfony2は`app/console`）、`web/app.php`、`web/app_dev.php`のエントリポイントとなるファイルの先頭で、`umask(0000);`としておくと、ファイルパーミッションが`0777`になるよと説明されています。

実際、`app/console`にも下記のコメントが既に埋まっていたりします。

```php
// app/console

// if you don't want to setup permissions the proper way, just uncomment the following PHP line
// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
umask(0000); // 今回ここのコメントを解除しました
```

## おわりに
まとめると、下記2つの対応をすることで`app/cache`に書き込めない問題を回避しました。

- `Capistrano`の`deploy:set_permissions:acl`は削除（何もしないようにする）
- `app/console`、`web/app.php`、`web/app_dev.php`の先頭に`umask(0000);`を追加

世界中のSymfonyユーザーのために、`capistrano-file-permissions`の新バージョンがリリースされることを希望します。
（みんな、umask使っているのかな。ひょっとして常識？）

ではでは。