概要
- 稼働中のDjangoアプリケーションをバージョンアップしました。ローカル環境で動作確認し問題なかったので、EC2サーバーでも
git pullして反映...しようとしたら、なぜか画面がInternal Server Errorになってしまいました。 - 結論、パーミッションの問題だったので、同様の悩みを抱える方の一助になればと思い、まとめておきます。
環境
- サーバー(AWS EC2インスタンス)
- OS:Ubuntu 18.04.3 LTS
- Web Server:Apache/2.4.29
- Django version: 3.0.5
エラー内容
- EC2サーバーで
git pullしてアプリを更新したら以下の画面になってしまいました。 - こんな画面です。Djangoの黄色いエラー画面ならともかく...これめちゃ焦るやん💦
原因の追求
- Apacheの
error.logやDjangoのlogファイルを見ても、原因がわかりません。Apacheはちゃんと起動していました。 - ローカル環境ではちゃんと動いていたので、やっぱりApache周りの問題だろうか...
- ちなみにApacheの設定ファイルなどは一切いじっていません。Djangoのソースコードを更新しただけです。
-
$ free -hで確認しましたが、メモリ不足でもありませんでした。 - 原因は意外なところにありました。djangoの
settings.pyでした...
前提
- 構造は以下の通り。
/var/www/html/sample_homepage/
└── homepage
├── homepage
│ └── settings.py
└── loggings
-
sample_homepageパーミッションを確認したところ、以下のようになっていました。
$ ls -l /var/www/html
drwxrwxr-x 10 root www-data 4096 Apr 6 11:55 sample_homepage
解決方法
-
settings.pyを以下のように変更してあげる必要がありました。 - 以下が問題だった箇所
#(省略)
log_filename = 'loggings/log_file_' + datetime.now().strftime("%Y-%m-%d") + '.log'
if not os.path.exists("loggings"):
os.makedirs("loggings")
#(省略)
↓
- 以下のように変更してあげます
#(省略)
log_filename = '/var/www/html/sample_homepage/homepage/loggings/log_file_' + datetime.now().strftime("%Y-%m-%d") + '.log'
if not os.path.exists("/var/www/html/sample_homepage/homepage/loggings"):
os.makedirs("/var/www/html/sample_homepage/homepage/loggings")
- 具体的には、相対パスから絶対パスにしています。
原因
- Internal Server Errorになる理由は、Pythonスクリプトがloggingsディレクトリを作成しようとした際に、Apacheがアクセス権限の問題を引き起こしているからでした。そのため、以下が解決策になります。
- (1) 相対パス(
"loggings")を使わずに絶対パス(/var/www/html/...)を使う(=上述の解決策) - (2) 適切なアクセス権限を設定する(=この場合は以下3通りの方法がある)
- ①
"www-data"ユーザーにログファイルの保存先を変更する - ②
"www-data"ユーザーに適切なアクセス権を与える - ③
"root"ユーザーにも書き込み権限を与える
- ①
- (1) 相対パス(
(1) 「相対パスを使わずに絶対パスを使う」をもう少し詳しく補足
- まず、大前提、以下の違いがあります。
-
"/var/www/html/sample_homepage/homepage/loggings":絶対パス。ファイルシステムのルートからの完全なパス。 -
"loggings":相対パス。現在の作業ディレクトリからの相対的なパス。
-
-
os.makedirs()関数を使用して新しいディレクトリを作成する場合- 相対パスを使用する場合:ディレクトリが作成される場所は現在の作業ディレクトリに依存
- 絶対パスを使用する場合:ディレクトリが作成される場所は絶対パスに依存
- つまり、
- 絶対パスを使用→
"www-data"ユーザーがアクセス権限を持っている場所にディレクトリを作成できる→成功 - 相対パスを使用→現在の作業ディレクトリがアプリケーションのディレクトリである場合でも、
"www-data"ユーザーがアクセス権限を持っている場所にディレクトリを作成することはできない("www-data"ユーザーがアクセス権限を持っている場所≠アプリケーションのディレクトリとは異なる場合がある)→失敗
- 絶対パスを使用→
相対パス失敗パターンの例
- 「
"www-data"ユーザーがアクセス権限を持っている場所≠アプリケーションのディレクトリとは異なる場合がある」と言うのは、例えば以下のような形です。- アプリケーションが
"/var/www/html"に配置されている -
"www-data"ユーザーが"/var/www/html/sample_homepage"に書き込み権限を持っている -
settings.pyで相対パス"loggings"と指定 - 実際には
"/var/www/html/sample_homepage/loggings"というパスが解決されてしまう -
"www-data"ユーザーが書き込み権限を持っているのは"/var/www/html/sample_homepage"であり、"/var/www/html/sample_homepage/loggings"には書き込み権限を持っていない(可能性がある)ため、os.makedirs()関数が失敗
- アプリケーションが
相対パスを使う場合は気をつけよう
-
上記の例のように、相対パスの場合は、
os.makedirs()関数が実行される場所からloggingsディレクトリにアクセスしようとします。しかし、この場合、os.makedirs()関数が実行されるのはApacheの実行時であり、通常は/var/www/htmlディレクトリになります。つまり、os.makedirs()関数が実行される場所もこのディレクトリ内になるため、loggingsディレクトリもこのディレクトリ内に作成されます。しかし、os.makedirs()関数がディレクトリを作成するとき、そのディレクトリの所有者は実行したプロセスのユーザーになります。つまり、rootユーザーがos.makedirs()関数を実行したため、loggingsディレクトリの所有者もrootユーザーになってしまいます。しかし、Apacheの実行ユーザーはwww-dataグループに所属しています。つまり、www-dataグループがアクセスできないrootユーザー所有のディレクトリやファイルは、Apacheからアクセスすることができないため、内部サーバーエラーが発生したのです。 -
絶対パスの場合は、ファイルやディレクトリにアクセスする場所が確実に指定されます。したがって、
/var/www/html/sample_homepage/homepage/loggingsディレクトリが存在しない場合でも、os.makedirs()関数によって作成されたディレクトリの所有者はrootではなく、www-dataグループになります。このため、Apacheの実行ユーザーであるwww-dataグループがアクセスできるようになります。 -
相対パスを使用する場合には、アプリケーションが配置されているディレクトリよりも上位のディレクトリに対して
"www-data"ユーザーが書き込み権限を持っていることを確認し、適切なパーミッションを設定する必要があります。 -
絶対パスを使用することで、
"www-data"ユーザーがアクセス権限を持っている場所にディレクトリを作成することができます。
(2) 「適切なアクセス権限を設定する」をもう少し詳しく補足
-
os.makedirs()関数は、指定されたパスに新しいディレクトリを再帰的に作成する関数です。この関数を使用すると、指定されたパスにディレクトリが存在しない場合、新しいディレクトリを作成することができます。 - Apacheは、ユーザー
"www-data"で動作しており、"www-data"ユーザーがディレクトリに書き込み権限を持っていない場合、os.makedirs()関数が失敗し、Internal Server Errorが発生します。ディレクトリに書き込み権限がないため、新しいディレクトリを作成できないためです。 - 上述の通り、
ls -lで確認したところ、以下のようになっていました。
drwxrwxr-x 10 root www-data 4096 Apr 6 11:55 sample_homepage
- 所有者が
"root"ユーザーで、グループ所有者が"www-data"になっています。つまり、"www-data"ユーザーはこのディレクトリに対して書き込み権限を持っていますが、"root"ユーザーは書き込み権限を持っていません。ログファイルの保存先としてこのディレクトリを使用する場合、"root"ユーザーがログファイルを作成することができないため、エラーが発生するわけですね。なので、以下のどれかを行う必要があります。- ①
"www-data"ユーザーにログファイルの保存先を変更する - ②
"www-data"ユーザーに適切なアクセス権を与える - ③
"root"ユーザーにも書き込み権限を与える
- ①
①"www-data"ユーザーにログファイルの保存先を変更するコマンド
- ログファイルの保存先を変更するには、例えば、以下のコマンドで可能です。
sudo mkdir /var/www/html/logs
sudo chown www-data:www-data /var/www/html/logs
- この場合、アプリケーションのログファイルの保存先を
"/var/www/html/logs"に変更すればOKです
②"www-data"ユーザーに適切なアクセス権を与えるコマンド
- 以下のコマンドで、指定したディレクトリ以下のすべてのファイルとディレクトリの所有者を
"www-data"ユーザーとグループに変更することができます。
sudo chown -R www-data:www-data /var/www/html/sample_homepage
③"root"ユーザーにも書き込み権限を与えるコマンド(最終手段)
- 以下のコマンドにより、他のユーザーに対して書き込み権限が与えられます。ただし、一般的にはセキュリティ上の理由から、Webアプリケーションを実行するためのディレクトリやファイルに対してrootユーザーに書き込み権限を与えることは推奨されていません。あくまで一つの方法として記載します。これは最終手段です。
sudo chmod o+w /var/www/html/sample_homepage
その他:アプリケーションの実行ユーザーを確認する(Ubuntuの場合)
- アプリケーションの実行ユーザーが期待しているユーザーではない可能性もあります。その場合は、以下を確認してください。
cd /etc/apache2
cat envvars
- 以下のように設定されていればOKです。
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
- パーミッションまわりは奥が深いですね。。
