akiyoko blog

akiyoko の IT技術系ブログです

本番運用しているブログサイトの Mezzanine を 4.1.0 から 4.2.2 にアップデートしてみた

Monotalk さんの以下の記事に触発されて、私が本番運用している某ブログサイトの Mezzanine を 4.1.0 から 4.2.2 にアップデートしてみました。


アップデートするのに結構苦労したように書いてあったので、念のため、Vagrant の開発環境と EC2 のバックアップイメージから起動したインスタンス上でのリハーサルをしてから臨みました。


結論から言うと、あまり苦労せずにアップデートできました。Monotalk さんは Mezzanine 3 系から運用していたような感じだったので、そこからの負債が溜まっていたのではないかと推測します。


 

アップデート手順

アップデートの手順はたったこれだけです。

$ pip install -U Mezzanine==4.2.2

$ python manage.py makemigrations
$ python manage.py migrate



アップデート前の本番環境のインストール済みライブラリ一覧(2016/11/2 時点)

$ pip list
awscli (1.11.10)
beautifulsoup4 (4.5.1)
bleach (1.4.3)
boto (2.41.0)
botocore (1.4.67)
chardet (2.3.0)
colorama (0.3.7)
Django (1.9.9)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.2)
django-ses (0.8.0)
docutils (0.12)
filebrowser-safe (0.4.5)
future (0.15.2)
futures (3.0.5)
grappelli-safe (0.4.4)
gunicorn (19.6.0)
html5lib (0.9999999)
jmespath (0.9.0)
Mezzanine (4.1.0)
MySQL-python (1.2.5)
oauthlib (1.1.2)
Pillow (3.3.1)
pip (8.1.2)
psycopg2 (2.6.2)
pyasn1 (0.1.9)
python-dateutil (2.5.3)
python-memcached (1.58)
pytz (2016.6.1)
rcssmin (1.0.6)
requests (2.11.1)
requests-oauthlib (0.6.2)
rjsmin (1.0.12)
rsa (3.4.2)
s3transfer (0.1.9)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.2.2)
wheel (0.29.0)


アップデート後のインストール済みライブラリの一覧は、以下のようになりました。

$ pip list
awscli (1.11.10)
beautifulsoup4 (4.5.1)
bleach (1.4.3)
boto (2.41.0)
botocore (1.4.67)
chardet (2.3.0)
colorama (0.3.7)
Django (1.10.3)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.3)
django-ses (0.8.0)
docutils (0.12)
filebrowser-safe (0.4.6)
future (0.16.0)
futures (3.0.5)
grappelli-safe (0.4.5)
gunicorn (19.6.0)
html5lib (0.9999999)
jmespath (0.9.0)
Mezzanine (4.2.2)
MySQL-python (1.2.5)
oauthlib (2.0.0)
Pillow (3.4.2)
pip (8.1.2)
psycopg2 (2.6.2)
pyasn1 (0.1.9)
python-dateutil (2.5.3)
python-memcached (1.58)
pytz (2016.7)
rcssmin (1.0.6)
requests (2.11.1)
requests-oauthlib (0.7.0)
rjsmin (1.0.12)
rsa (3.4.2)
s3transfer (0.1.9)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.3)
wheel (0.29.0)


Mezzanine 4.1.0 から 4.2.2 への一番の変更点は、Django のバージョンが 1.9 系から 1.10 系にアップデートされることでしょうか。

4.1.0 から 4.2.2 への全ての変更箇所は以下で確認することができます。
https://github.com/stephenmcd/mezzanine/compare/4.1.0...4.2.2


このアップデートでいくつかのバグ(*1)が本家で修正されたのに伴い、技術的な負債を少し解消することができました。



 

影響

4.2.2 へのアップデート後、ping_google コマンド実行時にエラーが出るようになってしまいました。

$ python manage.py ping_google
Traceback (most recent call last):
  File "manage.py", line 14, in <module>
    execute_from_command_line(sys.argv)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/contrib/sitemaps/management/commands/ping_google.py", line 12, in handle
    ping_google(sitemap_url=options['sitemap_url'])
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/contrib/sitemaps/__init__.py", line 36, in ping_google
    raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.")
django.contrib.sitemaps.SitemapNotFound: You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.

いろいろ調査したのですが、まだ解決していません。。

Django 本体の差分にも原因があるのかなぁ??
https://fossies.org/diffs/Django/1.9.8_vs_1.10/django/contrib/sitemaps/__init__.py-diff.html



 

バックアップイメージでの検証

EC2 のバックアップイメージから起動したインスタンス上でのリハーサル手順をメモしておきます。

1. バックアップイメージからインスタンス起動

まずは、EC2 インスタンスの Create Image をおこない、AMI を作成します。

作成したイメージからインスタンスを起動し、新たな Elastic IP を付与します。


 

2. settings.py の修正

config/local_settings.py

ALLOWED_HOSTS = ['akiyoko.com']

を、

ALLOWED_HOSTS = ['52.199.xx.xx']

と修正します(新しい IP アドレスを「52.199.xx.xx」と想定)。


最後に、プロセスを再起動。

$ sudo supervisorctl restart all


なおこれを修正しないと、アクセス時に Bad Request (400) が発生してしまいます。

(参考)django - Bad request 400: nginx / gunicorn - Stack Overflow

 

3. Nginx の設定ファイル修正

server_name に指定しているドメインを、Elastic IP に変更します。
また、テスト検証では HTTPS は使用しないので、SSL の設定は取り除きます。


/etc/nginx/sites-enabled/akiyokoproject.conf

(変更前)

server {

    #listen 80;
     listen 443 ssl;
    server_name akiyoko.com;
    client_max_body_size 10M;
    keepalive_timeout    15;
    error_log /home/webapp/logs/akiyokoproject_error_nginx.log info;

     ssl_certificate      conf/akiyokoproject.crt;
     ssl_certificate_key  conf/akiyokoproject.key;
     ssl_session_cache    shared:SSL:10m;
     ssl_session_timeout  10m;
     ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
     ssl_prefer_server_ciphers on;

    # Deny illegal Host headers
    if ($host !~* ^(akiyoko.com)$) {
        return 444;
    }
    ・
    ・

server {
    listen 80;
    server_name akiyoko.com;
    return 301 https://akiyoko.com$request_uri;
}

server {
    listen 80;
    listen 443 ssl;
    server_name www.akiyoko.com;
    return 301 https://akiyoko.com$request_uri;
}


(変更後)

server {

    listen 80;
    # listen 443 ssl;
    server_name 52.199.xx.xx;
    client_max_body_size 10M;
    keepalive_timeout    15;
    error_log /home/webapp/logs/akiyokoproject_error_nginx.log info;

     #ssl_certificate      conf/akiyokoproject.crt;
     #ssl_certificate_key  conf/akiyokoproject.key;
     #ssl_session_cache    shared:SSL:10m;
     #ssl_session_timeout  10m;
     #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
     #ssl_prefer_server_ciphers on;

    # Deny illegal Host headers
    if ($host !~* ^(52.199.xx.xx)$) {
        return 444;
    }
    ・
    ・

#server {
#    listen 80;
#    server_name akiyoko.com;
#    return 301 https://akiyoko.com$request_uri;
#}

#server {
#    listen 80;
#    listen 443 ssl;
#    server_name www.akiyoko.com;
#    return 301 https://akiyoko.com$request_uri;
#}


最後に Nginx をリロード。

$ sudo service nginx reload


 

4. Mezzanine アップデート

$ workon akiyokoproject
(akiyokoproject)$ pip install -U Mezzanine==4.2.2
  ・
  ・
Successfully installed Mezzanine-4.2.2 django-1.10.3 django-contrib-comments-1.7.3 filebrowser-safe-0.4.6 future-0.16.0 grappelli-safe-0.4.5 oauthlib-2.0.0 pillow-3.4.2 pytz-2016.7 requests-oauthlib-0.7.0 tzlocal-1.3
(akiyokoproject)$ python manage.py makemigrations
(akiyokoproject)$ python manage.py migrate

Mezzanine 系のモデル変更はありませんでしたが、Django 系の auth_user のモデルが少し変更になったようです。

$ sudo supervisorctl restart all


 

5. テンプレートの更新

HTMLファイル確認用の templats ディレクトリに、テンプレートの更新差分(4.1.0 → 4.2.2)をマージします。

config/settings.py

@@ -273,7 +275,7 @@ TEMPLATES = [
     {
         "BACKEND": "django.template.backends.django.DjangoTemplates",
         "DIRS": [
-            os.path.join(PROJECT_ROOT, "custom/templates"),
+            #os.path.join(PROJECT_ROOT, "custom/templates"),
             os.path.join(PROJECT_ROOT, "templates")
         ],
         "APP_DIRS": True,

上記のように修正して、一時的にカスタムテンプレートを外してから、

$ python manage.py collecttemplates

を実行して、テンプレートを上書きします。

なお、上書きするときにいちいちオーバーライドするかどうか聞かれるので、
~/.virtualenvs/akiyokoproject/lib/python2.7/site-packages/mezzanine/core/management/commands/collecttemplates.py

               self.stdout.write("Template exists%s.\n" % prev)
                #confirm = input("Overwrite?  (yes/no/abort): ")
                #while confirm not in ("yes", "no", "abort"):
                #    confirm = input(
                #        "Please enter either 'yes', 'no' or 'abort': ")
                #if confirm == "abort":
                #    self.stdout.write("Aborted\n")
                #    break  # exit templates copying loop
                #elif confirm == "no":
                #    self.stdout.write("[Skipped]\n")
                #    copy = False
            if copy:
                try:
                    os.makedirs(os.path.dirname(dest))
                except OSError:
                    pass
                shutil.copy2(path, dest)
                template_src[name] = app
                count += 1
        if verbosity >= 1:
            s = "s" if count != 1 else ""
            self.stdout.write("\nCopied %s template%s\n" % (count, s))

とコメントアウトすると、手間が省けます。


custom アプリケーションで独自に修正したテンプレートの一覧は以下の通りなので、

custom/__init__.py
custom/static/css/custom.css
custom/static/img/slide-1.jpg
custom/static/img/slide-2.jpg
custom/templates/base.html
custom/templates/blog/blog_post_detail.html
custom/templates/blog/blog_post_list.html
custom/templates/generic/includes/comment.html
custom/templates/generic/includes/comments.html
custom/templates/includes/form_fields.html
custom/templates/index.html
custom/templates/pages/menus/dropdown.html
custom/templates/twitter/tweets.html
custom/templatetags/__init__.py
custom/templatetags/add_attributes.py

本家テンプレートの更新差分(4.1.0 → 4.2.2)と重複したファイル

  • blog/blog_post_detail.html

については手動でマージをおこない、その他の

  • includes/editable_loader.html
  • pages/form.html
  • pages/menus/admin.html

については custom アプリケーションの templates ディレクトリ以下のものに上書きしました。