Python + Djangoで動画共有サイトを作る Part4
前回の続きです。視聴ページとデプロイをします。
視聴ページ作成
動画を再生する視聴ページを作成します。HTML5ではvideoタグを使うことでブラウザがUIを提供してくれます。
- video/templates/video/watch.html
{% extends "video/base.html" %} {% block title %}{{ content.title }} - Video{% endblock %} {% block header %} {% load static %} <script src="{% static 'video/jquery-3.4.1.min.js' %}"></script> <script src="{% static 'video/watch.js' %}"></script> {% endblock %} {% block main %} <br> <br> <h2>{{ content.title }}</h2><br> {{ content.description }} <hr> tags: {% for tag in tags %} <a href="../tag/{{ tag.tag.name }}">{{ tag.tag.name }}</a> {% endfor %} <br> <hr> <video id="video_content" src="/media/video/{{ content.id }}/{{ content.filename}}" poster="/media/video/{{ content.id }}/thumb.jpg" controls> 動画の再生にはHTML5が必要です。 </video> <hr> <br> {% endblock %}
再生画面で音量を変更した場合、その値を記憶するようにします。
- video/static/video/watch.js
$(function() { function getCookie() { var cookie = []; if (document.cookie != '') { cookie_list = document.cookie.split(';'); for (var item of cookie_list) { [key, value] = item.split('='); cookie[key.replace(' ', '')] = decodeURIComponent(value); } } return cookie; } function saveCookie(_key, _value) { document.cookie = _key + '=' + encodeURIComponent(_value) + '; max-age=31536000; path=/'; } $(document).ready(function(){ cookie = getCookie(); if (cookie['volume']) { $('#video_content').get(0).volume = cookie['volume']; } }); $('#video_content').on('volumechange', function() { saveCookie('volume', $('#video_content').get(0).volume); }); });
音量の値の記憶にはcookieを用います。
- video/views.py
def watch(request, content_id): content = get_object_or_404(VideoContent, pk=content_id) tags = VideoTagList.objects.filter(content_id=content_id).select_related('content') return render(request, 'video/watch.html', {'content':content, 'tags':tags})
コメントアウトを外した後にrunserverを実行してWebサイトにアクセスします。動画のリンクをクリックすると再生画面に飛びます。
デプロイ
いままで開発環境だったためrunserverを実行して動かしました。今後はHTTPサーバから呼び出す形で実行します。HTTPサーバとしてはapacheを使用します。
#yum install httpd python36u-mod_wsgi policycoreutils-python
インストールが完了したらデーモンとして起動します。
# sudo systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled) Active: inactive (dead) Docs: man:httpd(8) man:apachectl(8) # # sudo systemctl start httpd # sudo systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled) Active: active (running) since Tue 2019-08-27 18:36:40 JST; 2s ago Docs: man:httpd(8) man:apachectl(8) Main PID: 26104 (httpd) Status: "Processing requests..." CGroup: /system.slice/httpd.service tq26104 /usr/sbin/httpd -DFOREGROUND tq26105 /usr/sbin/httpd -DFOREGROUND tq26106 /usr/sbin/httpd -DFOREGROUND tq26107 /usr/sbin/httpd -DFOREGROUND tq26108 /usr/sbin/httpd -DFOREGROUND mq26109 /usr/sbin/httpd -DFOREGROUND Aug 27 18:36:40 localhost.localdomain systemd[1]: Starting The Apache HTTP Server... Aug 27 18:36:40 localhost.localdomain httpd[26104]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using l... message Aug 27 18:36:40 localhost.localdomain systemd[1]: Started The Apache HTTP Server. Hint: Some lines were ellipsized, use -l to show in full. # # sudo systemctl enable httpd Created symlink from /etc/systemd/system/multi-user.target.wants/httpd.service to /usr/lib/systemd/system/httpd.service. #
続いて、HTTPサーバでWSGIが動作するように設定を変更します。まずはロケールの設定を変更します。
- /etc/sysconfig/httpd
LANG=ja_JP.utf8
今回は日本語を使用するためロケールの文字コードをUTF8に変更します。この設定を入れないと文字をASCIIとして扱うためUnicodeEncodeError: 'ascii' codec can't encode characters in position 40-43: ordinal not in range(128)というエラーが発生します。
続いてHTTPサーバの設定をします。今回は/var/www/django/以下にファイルを設置します。
WSGIScriptAlias / /var/www/django/mysite/mysite/wsgi.py WSGIPythonPath /var/www/django/mysite/ <Directory "/var/www/django/mysite/video"> <Files wsgi.py> Require all granted </Files> </Directory> Alias /static/ /var/www/django/mysite/video/static/ <Directory "/var/www/django/mysite/video/static"> Require all granted </Directory> Alias /media/ /storage/ <Directory "/storage/"> Require all granted </Directory>
設定が完了したらHTTPサーバを再起動します。
# systemctl restart httpd # sudo systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled) Active: active (running) since Tue 2019-08-27 18:53:18 JST; 4s ago Docs: man:httpd(8) man:apachectl(8) Process: 26163 ExecStop=/bin/kill -WINCH ${MAINPID} (code=exited, status=0/SUCCESS) Main PID: 26167 (httpd) Status: "Processing requests..." CGroup: /system.slice/httpd.service tq26167 /usr/sbin/httpd -DFOREGROUND tq26168 /usr/sbin/httpd -DFOREGROUND tq26169 /usr/sbin/httpd -DFOREGROUND tq26170 /usr/sbin/httpd -DFOREGROUND tq26171 /usr/sbin/httpd -DFOREGROUND mq26172 /usr/sbin/httpd -DFOREGROUND Aug 27 18:53:17 localhost.localdomain systemd[1]: Stopped The Apache HTTP Server. Aug 27 18:53:17 localhost.localdomain systemd[1]: Starting The Apache HTTP Server... Aug 27 18:53:17 localhost.localdomain httpd[26167]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using l... message Aug 27 18:53:18 localhost.localdomain systemd[1]: Started The Apache HTTP Server. Hint: Some lines were ellipsized, use -l to show in full.
HTTPサーバの設定ファイルで指定したフォルダに作成したdjangoプロジェクトのファイルを設置します。
# mkdir /var/www/django # mkdir /var/www/django/mysite # cp * /var/www/django/mysite/ -R # chown apache:apache /var/www/django/mysite -R # ls -l /var/www/django/mysite /var/www/django/mysite: total 4 -rwxr-xr-x. 1 apache apache 626 Aug 27 19:01 manage.py drwxr-xr-x. 3 apache apache 93 Aug 27 19:01 mysite drwxr-xr-x. 6 apache apache 188 Aug 27 19:01 video #
また、ストレージについてもHTTPサーバからアクセスできるようにします。
# chown apache:apache -R /storage/video/ # semanage fcontext -a -t httpd_sys_rw_content_t '/storage/video(/.*)?' # restorecon -v -R /storage/video/
djangoの設定も公開用に書き換えます。
- mysite/settings.py
# SECURITY WARNING: don't run with debug turned on in production! DEBUG = False ALLOWED_HOSTS = ['192.168.12.9']
djangoではデプロイ時に設定をチェックする機能があります。
$ python3 manage.py check --deploy
必要に応じて警告の内容を修正します。修正後は設定内容を反映するためHTTPサーバを再起動する必要があります。
# systemctl restart httpd
サーバの設定が完了したのでファイアウォールの設定を変更します。
$ sudo firewall-cmd --info-zone=public public (active) target: default icmp-block-inversion: no interfaces: enp0s3 sources: services: ssh dhcpv6-client ports: 8000/tcp protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: $ sudo firewall-cmd --add-service http --zone=public --permanent success $ sudo firewall-cmd --reload success $ sudo firewall-cmd --info-zone=public public (active) target: default icmp-block-inversion: no interfaces: enp0s3 sources: services: ssh dhcpv6-client http ports: 8000/tcp protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
今までのrunserverではhttp://192.168.12.9:8000/でアクセスしていましたが、HTTPサーバに配置したのでhttp://192.168.12.9/にアクセスします。 正常に表示・動作できていればOKです。
トラブルシューティング
ファイルが存在するのに500 Internal Errorになる
サーバのエラーに関する情報はerror_logに記録されています。
# tailf /var/log/httpd/error_log [Tue Aug 27 19:13:30.382093 2019] [core:error] [pid 26296] (13)Permission denied: [client 192.168.12.4:57900] AH00035: access to /media/video/3/thumb.jpg denied (filesystem path '/storage/video/3/thumb.jpg') because search permissions are missing on a component of the path, referer: http://192.168.12.9/video/ [Tue Aug 27 19:13:31.346448 2019] [wsgi:error] [pid 26296] [client 192.168.12.4:57900] Not Found: /favicon.ico
Permission deniedの場合は権限に由来するものです。拒否されたファイルを調査します。
# ls -lZ storage/video/3/ -rw-------. root root unconfined_u:object_r:user_tmp_t:s0 video.mp4 -rw-------. root root unconfined_u:object_r:default_t:s0 thumb.jpg
apache(HTTPサーバ)が所有者になっていない場合はchmodコマンドで所有者を変更します。
# chmod apache:apache /storage/video/3/video.mp4 # chmod apache:apache /storage/video/3/thumb.jpg # ls -lZ /storage/video/3/ -rw-------. apache apache unconfined_u:object_r:user_tmp_t:s0 video.mp4 -rw-------. apache apache unconfined_u:object_r:default_t:s0 thumb.jpg
また、SELinuxの権限が異なる場合もアクセスが拒否されます。SELinuxが動作しているかは次のコマンドで確認できます。
# getenforce Enforcing
EnforcingであればSELinuxが動作しています。SELinuxは一時的に停止することができます。
# setenforce 0 # getenforce Permissive
PermissiveであればSELinuxによるアクセス拒否は発生しません。アクセスに関するログはaudit.logに記録されています。
# tailf /var/log/httpd/error_log type=AVC msg=audit(1566901506.108:729): avc: denied { getattr } for pid=26343 comm="httpd" path="/storage/video/3/thumb.jpg" dev="dm-0" ino=17209734 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=1
HTTPサーバのコンテキストsystem_u:system_r:httpd_tではファイルのコンテキストunconfined_u:object_r:default_t:s0を読みことができないため拒否(denied)されています。一時的にファイルのコンテキストを変更してみます。
# chcon -t httpd_sys_content_t /storage/video/3/thumb.jpg # ls -lZ /storage/video/3/ -rw-------. apache apache unconfined_u:object_r:user_tmp_t:s0 video.mp4 -rw-------. apache apache unconfined_u:object_r:httpd_sys_content_t thumb.jpg
これでコンテキストが変更されたので再度ブラウザからアクセスし、audit.logに拒否の記録が残るかどうか確かめます。問題なく表示された場合はSELinuxの設定を戻します。
# setenforce 1 # getenforce Enforcing
chconは一時的なものなので新しくSELinuxのルールを作成します。
# semanage fcontext -a -t httpd_sys_rw_content_t '/storage/video(/.*)?' # semanage fcontext -l | grep /storage/ /storage/video(/.*)? all files system_u:object_r:httpd_sys_rw_content_t:s0
続いて、設定したルールを適用します。
# restorecon -v -R /storage/video/ restorecon reset /storage/video/3/video.mp4 context unconfined_u:object_r:user_tmp_t:s0->unconfined_u:object_r:httpd_sys_rw_content_t:s0
他のファイルのコンテキストもルールに従って再設定されました。