akiyoko blog

akiyoko の IT技術系ブログです

Mezzanine の本番設定(その3:Mezzanine の本番デプロイ)〜AWS 環境構築から運用設定まで〜

こんにちは、akiyoko です。

Mezzanine は、知る人ぞ知る Python製の WordPress風フルスタックCMSフレームワークです。

akiyoko.hatenablog.jp


今年の 7月に、Mezzanine を使った某ブログサイト(将来的に ECサイトを増設予定)の本番運用を開始しました。*1 その備忘録として、AWS の初期設定から Mezzanine の本番デプロイ、ちょっとした運用設定までの記録をまとめておくことにしました。

全ての記録を一つの記事にすると長くなり過ぎるので、テーマごとに、

の 4本に記事を分割することにしました。

今回はその 3本目、「その3:Mezzanine の本番デプロイ」について説明します。


Mezzanine の本番デプロイとして実施した内容としては、

  • 本番サーバ(Ubuntu)を起動
  • Fabric スクリプトを実行
  • Fabric スクリプト実行後の各種設定
    • SSL の設定
    • URL の転送設定
    • BASIC認証の設定
    • MySQL のインストール
    • メールの設定
    • ログの設定
    • タイムゾーンを Asia/Tokyo に変更
    • sitemap.xml / robot.txt の設定

となります。



【目次】


 

1. 概要

Mezzanine には何と、コマンド一発で リバースプロキシや DB などを含めた全部入りの本番環境が構築できる Fabric スクリプト が含まれています。

Fabric

Each Mezzanine project comes bundled with utilities for deploying production Mezzanine sites, using Fabric. The provided fabfile.py contains composable commands that can be used to set up all the system-level requirements on a new Debian based system, manage each of the project-level virtual environments for initial and continuous deployments, and much more.


(akiyoko 翻訳)
それぞれの Mezzanine プロジェクトには、本番 Mezzanine サイトをデプロイするための Fabric を使った便利ツールがバンドルされています。提供されている fabfile.py には、新しい Debian ベースのシステム上のあらゆるシステムレベルの依存ライブラリをセットアップしたり、初回デプロイ・継続的デプロイのためのプロジェクトレベルの仮想環境を管理したり、他にもたくさんのことをしたりするために利用できる組み立て可能なコマンド群が含まれています。


Deployment — Mezzanine 4.2.3 documentation


 
Mezzanine の Fabric スクリプト (*2) を実行するには、Fabric を pip でインストールした後、

$ fab secure

してから、

$ fab all

するだけです。簡単ですよね!


一度試してみれば分かりますが、もう至れり尽くせりです。
例えば、HTTPS のためのオレオレ証明書を作成して所定の位置に配置し、Nginx の設定までしてくれるという心尽くしに「ありがとう!」と思わず声が漏れてしまいます。



Mezzanine デフォルトの Fabric スクリプトを実行して構築した本番環境は、以下のコンポーネントで構成されます。

  • NGINX - public facing web server
  • gunicorn - internal HTTP application server
  • PostgreSQL - database server
  • memcached - in-memory caching server
  • supervisord - process control and monitor
  • virtualenv - isolated Python environments for each project
  • git or mercurial - version control systems (optional)


Deployment — Mezzanine 4.2.3 documentation


データベースとして PostgreSQL がインストールされますが、PostgreSQL と MySQL はどっちがいいの?という議論(*3)は置いておいて、私の場合は単純に「操作や運用に慣れている」という理由から、今回はデータベースを PostgreSQL から MySQL に入れ替えることにしました(実際には、Fabric スクリプトを実行して PostgreSQL を一旦インストールした後に手動で削除し、Ansible で MySQL をインストールしました)。


(参考)


 
最終的なサーバとコンポーネント、プロジェクトの構成は以下のようになりました。

サーバ:

  • Ubuntu 14.04.4 LTS


コンポーネント:

Nginx 1.4.6
Gunicorn 19.6.0
MySQL 5.5.49
Supervisor 3.0b2
Python 2.7.6
Django 1.9.7
Mezzanine 4.1.0 (*5

上記のバージョンは、いずれも 2016年7月時点のものです。


プロジェクト:

プロジェクト名 akiyokoproject
プロジェクトの配置場所 /home/webapp/mezzanine/akiyokoproject
プロジェクトのオーナ(Ubuntu ユーザ名) webapp
virtualenv の配置場所 /home/webapp/.virtualenvs/akiyokoproject
デプロイ先のドメイン名 akiyoko.com (*6


以降、Fabric スクリプトの実行を含めた、Mezzanine の本番デプロイの手順について説明します。


 

2. Ubuntu インスタンスを起動

Fabric スクリプトを実行する前に、スクリプトを流し込むサーバを立ち上げる必要があります。サーバには、AWS EC2 で起動した Ubuntu インスタンスを利用します。

また初期設定として、Fabric スクリプトを流し込むための SSHユーザの設定などを行います。

 

2.1. EC2インスタンスを起動

AWS EC2インスタンスを立ち上げます。その際、IAMロール「ec2-prod」(*7)を付けておきます。

具体的な起動方法については、以下の過去記事を参照してください。

<過去記事>
akiyoko.hatenablog.jp


以下、インスタンスの IPアドレスを「52.xxx.xxx.xxx」とします。

2.2. webapp ユーザを作成

以下のコマンドで Ubuntu にアクセスします。
(サーバにアクセスするための SSH秘密鍵が「~/.ssh/aws_p1.pem」に配置されているという前提です。)

$ ssh -i ~/.ssh/aws_p1.pem ubuntu@52.xxx.xxx.xxx

Fabric スクリプトの各コマンドを実行する SSHユーザ「webapp」を作成します。

$ sudo adduser --gecos '' webapp

(ここでは、パスワードは「pass」など簡単なもので OK。)

(参考)What do the `--disabled-login` and `--gecos` options of `adduser` command stand for? - Ask Ubuntu


次に、webapp ユーザがパスワード無しでログインできるように設定します。

$ sudo visudo -f /etc/sudoers.d/90-cloud-init-users

を実行して、以下の一行を末尾に追加します(control + option + x で保存)。

webapp ALL=(ALL) NOPASSWD:ALL

Ubuntu サーバを再起動後、ubuntu ユーザのものと同じ SSH鍵でログインできるように設定します。 *8

$ sudo service sudo restart

$ sudo su - webapp
$ mkdir -m 700 ~/.ssh
$ sudo cp -a /home/ubuntu/.ssh/authorized_keys ~/.ssh/
$ sudo chown webapp. ~/.ssh/authorized_keys
$ sudo chmod 600 ~/.ssh/authorized_keys

最後に、Mac 上から

$ ssh -i ~/.ssh/aws_p1.pem webapp@52.xxx.xxx.xxx

でサーバにログインできることが確認できれば OK。




 

3. Fabric スクリプトを実行

3.1. 本番デプロイ用の Mezzanine プロジェクトを作成

Mac上で、Fabric スクリプトを実行するための、本番デプロイ用の Mezzanine プロジェクトを作成します。

### virualenv が Mac にインストール済みという前提
$ virtualenv ~/.venvs/mezzanine_deploy
$ source ~/.venvs/mezzanine_deploy/bin/activate

### Fabric を実行するために必要なコンポーネントを pip install
(mezzanine_deploy)$ pip install future fabric mezzanine

Mezzanine (4.1.0)
Fabric (1.11.1)
paramiko (1.17.0)

### 適当なディレクトリにプロジェクトを作成
(mezzanine_deploy)$ cd ~/dev
(mezzanine_deploy)$ mkdir mezzanine_deploy_akiyokoproject_for_aws && cd $_
(mezzanine_deploy)$ mezzanine-project config .


本番デプロイ用に、config/local_settings.py を以下のように編集します。 *9

    ・
    ・
###################
# DEPLOY SETTINGS #
###################

# Domains for public site
ALLOWED_HOSTS = [".akiyoko.com"]

# These settings are used by the default fabfile.py provided.
# Check fabfile.py for defaults.

FABRIC = {
    "SSH_USER": "webapp",  # VPS SSH username and will be an application owner
    "SSH_KEY_PATH": "~/.ssh/aws_p1.pem",
    "HOSTS": ["52.xxx.xxx.xxx"],  # The IP address of your VPS
    "DOMAINS": ["akiyoko.com"],  # Edit domains in ALLOWED_HOSTS
    #"LIVE_HOSTNAME": "akiyoko.com", # Host for public site.
    #"VIRTUALENV_HOME": "/home/ubuntu/.virtualenvs",  # Absolute remote path for virtualenvs
    "PROJECT_NAME": "akiyokoproject", # Unique identifier for project
    "REQUIREMENTS_PATH": "requirements.txt",  # Project's pip requirements
    "GUNICORN_PORT": 8000, # Port gunicorn will listen on
    "LOCALE": "en_US.UTF-8",  # Should end with ".UTF-8"
    "DB_PASS": "dbpass",  # Live database password
    "ADMIN_PASS": "adminpass",  # Live admin user password
    "SECRET_KEY": SECRET_KEY,
    "NEVERCACHE_KEY": NEVERCACHE_KEY,
}

(「ADMIN_PASS」および「ADMIN_PASS」の値はサンプルですので、コピペしないようにご注意ください。)


ちなみに、

    "DEPLOY_TOOL": "git",

と設定すると、

Fatal error: local() encountered an error (return code 128) while executing 'git push -f ssh://ubuntu@52.xxx.xxx.xxx/home/ubuntu/git/akiyokoproject.git master'

というエラーになったので、今回は設定していません。


 

3.2. fabfile.py を修正

2016年7月時点では、fab all 実行時に、

$ /home/webapp/mezzanine/akiyokoproject/bin/python /home/webapp/mezzanine/akiyokoproject/manage.py syncdb --noinput ->

[52.xxx.xxx.xxx] out: Unknown command: 'syncdb'
[52.xxx.xxx.xxx] out: Type 'manage.py help' for usage.
[52.xxx.xxx.xxx] out:

Fatal error: run() received nonzero return code 1 while executing!

というエラーが出たので、fabfile.py を以下のように修正しました。 *10


fabfile.py

@@ -435,6 +435,7 @@ def install():
     """
     # Install system requirements
     sudo("apt-get update -y -q")
+    sudo("apt-get upgrade -y -q")
     apt("nginx libjpeg-dev python-dev python-setuptools git-core "
         "postgresql libpq-dev memcached supervisor python-pip")
     run("mkdir -p /home/%s/logs" % env.user)
@@ -635,7 +636,7 @@ def deploy():
             rsync_upload()
     with project():
         manage("collectstatic -v 0 --noinput")
-        manage("syncdb --noinput")
+        manage("makemigrations --noinput")
         manage("migrate --noinput")
     for name in get_templates():
         upload_template_and_reload(name)

原因は、fabfile.py 内で使用されている syncdb コマンドが Django 1.9(Django 1.7 以降)に対応していないからです。

(参考)Deploy Mezzanine to Ubuntu 14 server on DigitalOcean with Fabric | PerezProgramming


なお、今回のように AWS + Ubuntu でサーバを構築する場合は、fab secure の内容はほぼ網羅できているのでスキップした(fab secure コマンドは実行しなかった)のですが、それだと「apt-get upgrade -y -q」が唯一実行できていないので、fabfile.py の install() に追加しています。


 

3.3. デプロイ実行

先述のように、今回は AWS + Ubuntu でサーバを構築したので、

(mezzanine_deploy)$ fab secure

は今回実行しません。


以下のコマンドのみを実行します。

(mezzanine_deploy)$ fab all

正常に Fabric スクリプトが流れ終わったら、デプロイ後の後処理を実施していきます。


 

4. SSL の設定

ここではすでに、正規の SSL証明書を取得しているものとします。
取得方法については、以下の過去記事を参考にしてください。

<過去記事>
akiyoko.hatenablog.jp



まずは、「GoGetSSL」の管理ページから、www_akiyoko_com.crt(SSL証明書)と www_akiyoko_com.ca-bundle(中間証明書)をダウンロードしておきます。


クライアント(Mac)からサーバに SSL証明書と中間証明書を転送します。

$ scp -i ~/.ssh/aws_p1.pem ~/Downloads/www_akiyoko_com.crt ubuntu@52.xxx.xxx.xxx:/tmp/
$ scp -i ~/.ssh/aws_p1.pem ~/Downloads/www_akiyoko_com.ca-bundle ubuntu@52.xxx.xxx.xxx:/tmp/

次に、Ubuntu 上で、SSL証明書を(Fabric スクリプトが作成してくれたデフォルトのものから)入れ替えます。

$ sudo mv /etc/nginx/conf/akiyokoproject.crt /etc/nginx/conf/akiyokoproject.crt.orig
$ sudo mv /etc/nginx/conf/akiyokoproject.key /etc/nginx/conf/akiyokoproject.key.orig
$ sudo cp /tmp/www_akiyoko_com.crt /etc/nginx/conf/akiyokoproject.crt
$ sudo chmod 644 /etc/nginx/conf/akiyokoproject.crt

### 中間証明書を連結する
$ cat /tmp/www_akiyoko_com.ca-bundle | sudo tee -a /etc/nginx/conf/akiyokoproject.crt

なお、Android 実機(Nexus 5)から HTTPS で閲覧したときに証明書エラーが出たので、以下を参考に、SSL証明書に中間証明書を連結しました。 *11

(参考)



最後に、秘密鍵を書き換えて(*12)、Nginx をリロードします。

$ sudo vi /etc/nginx/conf/akiyokoproject.key
(秘密鍵をコピペ)

$ ls -al /etc/nginx/conf
total 24
drwxr-xr-x 2 root root 4096 Jul  7 15:29 .
drwxr-xr-x 6 root root 4096 Jul  7 15:14 ..
-rw-r--r-- 1 root root 1964 Jul  7 15:29 akiyokoproject.crt
-rw-r--r-- 1 root root 1111 Jul  7 15:14 akiyokoproject.crt.orig
-rw-r--r-- 1 root root 1704 Jul  7 15:29 akiyokoproject.key
-rw-r--r-- 1 root root 1704 Jul  7 15:14 akiyokoproject.key.orig

### Nginx をリロード
$ sudo service nginx reload


 

5. URL の転送設定

ここで、

http://akiyoko.com/foo/bar ⇒ https://akiyoko.com/foo/bar
http://www.akiyoko.com/foo/bar ⇒ https://akiyoko.com/foo/bar
https://www.akiyoko.com/foo/bar ⇒ https://akiyoko.com/foo/bar

のように URL転送(要するに全部 https の wwwなしドメインに転送)したいので、Nginx の設定を修正して 80 番ポートアクセス時の転送先を変更します。


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

server {

    #listen 80;
     listen 443 ssl;

    ・
    ・

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 {
    listen 80;
    server_name akiyoko.com;
    return 301 https://akiyoko.com$request_uri;
}

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

一旦、Nginx をリロードします。

$ sudo service nginx reload


 

6. BASIC認証の設定

(デプロイ時はまだリリース前だったので)ここで一旦、BASIC認証をかけておきます。もちろん、サイト公開時には設定を外しておくのを忘れずに。

$ sudo apt-get -y install apache2-utils
$ sudo htpasswd -c /etc/nginx/.htpasswd test
(パスワードを入力)


Nginx の設定を変更します。

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

server {

    #listen 80;
     listen 443 ssl;
    server_name akiyoko.com;

    ・
    ・

    location / {
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;

        proxy_redirect      off;
        proxy_set_header    Host                    $host;
        proxy_set_header    X-Real-IP               $remote_addr;
        proxy_set_header    X-Forwarded-For         $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Protocol    $scheme;
        proxy_pass          http://akiyokoproject;
    }}


Nginx をリロードして作業完了です。

$ sudo service nginx reload


(参考)Nginx で Basic 認証 - Qiita



 

7. MySQL のインストール

先述の通り今回は、Fabric スクリプトを実行してインストールされる PostgreSQL から MySQL にデータベースを入れ替えました。実際の作業としては、インストールされた PostgreSQL を手動で削除し、Ansible スクリプトを実行して MySQL をインストールしました。

以下、その手順です。

7.1. PostgreSQL のアンインストール

まず、手動で PostgreSQL を削除します。

$ sudo apt-get -y --purge remove postgresql\*
$ sudo rm -rf /etc/postgresql/
$ sudo rm -rf /etc/postgresql-common/
$ sudo rm -rf /var/lib/postgresql/
$ sudo userdel -r postgres
$ sudo groupdel postgres


 

7.2. MySQL のインストール

次に、MySQL を Ansible を使って Mac からインストールします。
今回は、Ansible Galaxy を使ってインストールをおこないました。

<過去記事>
akiyoko.hatenablog.jp



Mac 上の適当なディレクトリにプロジェクトを作成します。

$ cd ~/dev/ansible-mysql

hosts

[db-servers]
52.xxx.xxx.xxx

site.yml

- hosts: db-servers
  user: ubuntu
  vars_files:
    - vars/database.yml
  roles:
    - { role: ANXS.mysql }

vars/database.yml

mysql_current_root_password: ''
mysql_root_password: dbpass
mysql_databases:
  - name: akiyokoproject
mysql_users:
  - name: akiyokoproject
    pass: akiyokoprojectpass
    priv: "akiyokoproject.*:ALL"

(「mysql_root_password」および「pass」の値はサンプルですので、コピペしないようにご注意ください。)



Ansible を実行します。

$ ansible-playbook -i hosts site.yml --sudo --private-key ~/.ssh/aws_p1.pem -vv

 

7.3. MySQL 用の設定

データベースを MySQL に変更したのに伴い、Ubuntu サーバの各種設定を変更します。


MySQL 用のプラグインをインストールします。

$ sudo apt-get -y install libmysqlclient-dev
$ pip install MySQL-python



settings.py のデータベース設定を修正します。

config/local_settings.py

    ・
    ・
DATABASES = {
    "default": {
        # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
        "ENGINE": "django.db.backends.mysql",
        # DB name or path to database file if using sqlite3.
        "NAME": "akiyokoproject",
        # Not used with sqlite3.
        "USER": "akiyokoproject",
        # Not used with sqlite3.
        "PASSWORD": "akiyokoprojectpass",
        # Set to empty string for localhost. Not used with sqlite3.
        "HOST": "127.0.0.1",
        # Set to empty string for default. Not used with sqlite3.
        "PORT": "",
    }
}
    ・
    ・

データベースのマイグレーションを実行します。

$ python manage.py migrate
$ python manage.py createsuperuser

username (leave blank to use 'webapp'): admin
Email address: admin@akiyoko.com
Password: 
Password (again):


とりあえずデータベースをバックアップしておきます。

$ mkdir ~/db_backup
$ mysqldump --single-transaction -u root -p akiyokoproject > ~/db_backup/akiyokoproject_init.dump

### タイムスタンプをバックアップファイル名に含める場合
$ mysqldump --single-transaction -u root -p akiyokoproject > ~/db_backup/akiyokoproject_backup_`date +%y%m%d%H%M`.dump

### リストアするときのコマンド
### mysql -u root -p akiyokoproject < ~/db_backup/akiyokoproject_init.dump

(参考)MySQLのデータベースをmysqldumpでバックアップ/復元する方法 | WEB ARCH LABO



最後に gunicorn を再起動して完了です。

(akiyokoproject)$ python manage.py collectstatic
$ sudo supervisorctl restart all




ここで万が一(バックアップした AMI から起動した直後など?)、virtualenvwrapper を使って workon しようとした際に、

$ workon akiyokoproject
workon: command not found

というエラーが出てしまう場合は、一旦 Ubuntu からログアウトして、ubuntuユーザで接続し直してから、

$ sudo su - webapp

として .bashrc を読み込み直すとよさそうです(source ~/.bashrc としてもよかったのかな??)。



 

8. メールの設定

前提として、AWS Management Console で SES の設定が完了し、起動した EC2インスタンスに SES の権限を持った IAMロール(今回は「ec2-prod」がそれに相当)が付与されているものとします。

<過去記事>
akiyoko.hatenablog.jp


なお、Amazon SES を利用するため、postfix などの MTA のインストールは不要です。


(参考)



Ubuntuサーバ上で、django-ses をインストールします。

(akiyokoproject)$ pip install django-ses


config/local_settings.py の末尾に以下を追加します。

################
# AWS SETTINGS #
################
#AWS_ACCESS_KEY_ID = "AKIxxxxxxxxxxxxxxxxx"
#AWS_SECRET_ACCESS_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

##################
# EMAIL SETTINGS #
##################
EMAIL_BACKEND = "django_ses.SESBackend"
AWS_SES_REGION_NAME = "us-west-2"
AWS_SES_REGION_ENDPOINT = "email.us-west-2.amazonaws.com"
#EMAIL_HOST_USER = "admin@akiyoko.com"
#EMAIL_USE_TLS = True
#EMAIL_HOST = "email-smtp.us-west-2.amazonaws.com"
#DEFAULT_FROM_EMAIL = SERVER_EMAIL = u"no-reply <no-reply@akiyoko.com>"

上記は、SES を「Oregon」リージョンで開設した場合の設定になります。

なお、コメントアウト部分はあってもなくても OK なのですが、「EMAIL SETTINGS」に設定している 3つは最低限必要となります。 *13

あと、「AWS_SES_REGION_ENDPOINT」は、普段(?)のホスト名とは違う(「email.」で始まる)ので注意が必要かと。


(参考)Customizing Mezzanine | ROSS LAIRD

の記事が、一番参考になりました。

その他、

も参考に。




 

9. ログの設定

Fabric スクリプト実行後の状態ではアプリケーションの各種ログが出力されないようになっているので、ここで、Mezzanine のログ設定を変更します。


まず、ログを出力するディレクトリを掘っておきます。
なお、gunicorn のプロセスは webapp ユーザで起動されるので、ディレクトリのオーナーを webapp.webapp にしておきます。

$ sudo mkdir /var/log/akiyokoproject
$ sudo chown webapp. /var/log/akiyokoproject


config/local_settings.py の末尾に、以下の設定を追加します。

################
# LOG SETTINGS #
################
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/var/log/akiyokoproject/application.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

(上記は、DEBUGレベル以上のログを「/var/log/akiyokoproject/application.log」に出力する設定になります。)


gunicorn を再起動して設定を反映させます。

$ sudo supervisorctl restart all


 

10. タイムゾーンを Asia/Tokyo に変更

これはちょっと「やっちまった系」です。

本番稼働して何日か経ってからふと思い立ってタイムゾーンを「Asia/Tokyo」に変更したのですが、本来は、Fabric スクリプトを実行する前にやっておくべきだったかもしれません。。

10.1. Ubuntuサーバのタイムゾーンを変更

$ timedatectl
      Local time: Mon 2016-07-25 13:02:02 UTC
  Universal time: Mon 2016-07-25 13:02:02 UTC
        Timezone: Etc/UTC (UTC, +0000)
     NTP enabled: yes
NTP synchronized: no
 RTC in local TZ: no
      DST active: n/a

$ sudo timedatectl set-timezone Asia/Tokyo

$ timedatectl
      Local time: Mon 2016-07-25 22:02:44 JST
  Universal time: Mon 2016-07-25 13:02:44 UTC
        Timezone: Asia/Tokyo (JST, +0900)
     NTP enabled: yes
NTP synchronized: no
 RTC in local TZ: no
      DST active: n/a

$ date
Mon Jul 25 22:05:10 JST 2016

(参考)


 

10.2. settings.py の修正

settings.py の TIME_ZONE を「Asia/Tokyo」に変更して、プロセスを再起動します。

config/settings.py

@@ -101,7 +101,7 @@ ALLOWED_HOSTS = []
 # timezone as the operating system.
 # If running in a Windows environment this must be set to the same as your
 # system time zone.
-TIME_ZONE = 'UTC'
+TIME_ZONE = 'Asia/Tokyo'

 # If you set this to True, Django will use timezone-aware datetimes.
 USE_TZ = True

 

10.3. MySQL のタイムゾーン変更

本番運用を開始した後に Ubuntu のタイムゾーンを変更したからか(十中八九それが原因だと思いますが・・)、Django Admin で [Content] > [Blog posts] に移動しようとすると、「Sorry, an error occurred.」と画面に出て、以下のエラーログが出るようになってしまいました。

    ・
    ・
ValueError: Database returned an invalid value in QuerySet.datetimes(). Are time zone definitions for your database and pytz installed?
Exception while resolving variable 'get_admin_url' in template 'includes/editable_toolbar.html'.
Traceback (most recent call last):
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/template/base.py", line 905, in _resolve_lookup
    (bit, current))  # missing attribute
VariableDoesNotExist: Failed lookup for key [get_admin_url] in u'None'

 
先に、MySQL の設定を確認しておきます。

$ mysql -u root -p
mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | JST    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.00 sec)

mysql> select count(*) from `mysql`.`time_zone_name`;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)


対策として、タイムゾーンデータをインポートしました(ちなみに、最後の「mysql」はテーブル名)。 *14

$ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Enter password:
Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.

### ↓のコマンドを実行すると、MySQLを再起動しなくてもOK
$ mysql -u root -p -e "flush tables;" mysql

(参考)mysql - Database returned an invalid value in QuerySet.dates() - Stack Overflow


なお、「MySQLでタイムゾーンを設定する - Qiita」に書かれているように、「/etc/my.cnf」のタイムゾーンを「default-time-zone = 'Asia/Tokyo'」などと追加しなくてもよさそうです(詳細は不明)。


最後に、MySQL の設定を再び確認。

$ mysql -u root -p
mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | JST    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.00 sec)

mysql> select count(*) from `mysql`.`time_zone_name`;
+----------+
| count(*) |
+----------+
|     1808 |
+----------+
1 row in set (0.00 sec)

(「/etc/my.cnf」のタイムゾーンを変更すると、time_zone の値は「Asia/Tokyo」になりますが)time_zone は「SYSTEM」のままです。


これで解決しました!!





 

11. sitemap.xml / robot.txt の設定

これは公開直前におこなう作業ですが、Google Search Console の設定に先駆けて、sitemap.xml および robot.txt の設定をしておきます。


これまたありがたいことに、Mezzanine には、sitemap.xml および robot.txt を提供してくれる仕組みがすでに整っています。


11.1. sitemap.xml の設定

sitemap.xml については、Mezzanine にデフォルトで「django.contrib.sitemaps」がインストールされているので、「/sitemap.xml」にアクセスするとコンテンツに応じた sitemap.xml が自動的に生成されて表示されます。

しかしながら Mezzanine 4.1.0 では、「/sitemap.xml」にアクセスすると以下のエラーが出てしまいます。

Internal Server Error: /sitemap.xml
Traceback (most recent call last):
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
    (中略)
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/mezzanine/core/sitemaps.py", line 27, in items
    return list(Displayable.objects.url_map(in_sitemap=True).values())
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/mezzanine/core/managers.py", line 366, in url_map
    home = self.model(title=_("Home"))
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/db/models/base.py", line 416, in __init__
    val = field.get_default()
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 905, in get_default
    if isinstance(field_default, self.remote_field.model):
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
Exception while resolving variable 'get_admin_url' in template 'includes/editable_toolbar.html'.
Traceback (most recent call last):
  File "/home/webapp/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/template/base.py", line 905, in _resolve_lookup
    (bit, current))  # missing attribute
VariableDoesNotExist: Failed lookup for key [get_admin_url] in u'None'


この不具合は本家で修正されていたのですが、残念ながら、私が本番デプロイした Mezzanine 4.1.0 にはまだマージされていませんでした(4.2.0 から適用されています)。
Fix dummy homepage object in sitemap. Closes #1516. · stephenmcd/mezzanine@94d5729 · GitHub


そこで以下の手順で、Mezzanine 4.1.0 にパッチを充てました。


まず、GitHub 上で stephenmcd/mezzanine を clone しておきます。
その後、Mac 上で以下のコマンドを実行します。

### ローカルに clone
$ git clone https://github.com/akiyoko/mezzanine.git

### tag 4.1.0 でチェックアウト(ブランチ名は「release」)
$ git checkout -b release refs/tags/4.1.0

### パッチを充てる
$ git cherry-pick 94d57294bcc1e934fdd723546be7ea15bb9dcd1a

### リモートリポジトリに push
$ git push origin release


本番環境にて、akiyoko リポジトリから release タグ指定で Mezzanine をインストールし直します。

$ workon akiyokoproject
$ pip install -U git+https://github.com/akiyoko/mezzanine.git@release#egg=Mezzanine

これで解決しました。


(参考)


 

11.2. robot.txt の設定

Fabric スクリプトを実行することで配置される robots.txt は、Google 検索エンジン bot のクローリングを全てのページで拒否するような設定になっているため、本番サイトの公開前に、以下のように「/admin/」以外をクローリング許可するような設定に書き換えます。


static/robots.txt

User-agent: *
Disallow:

となっているのを、

User-agent: *
Disallow: /admin/

Sitemap: https://akiyoko.com/sitemap.xml

に修正します。
(なお、「static/robots.txt」は、.gitignore に「/static」が含まれているため Git管理下にはなりません。)





 

12. デプロイ後の本番環境確認

最後に、デプロイ後の本番環境の各種設定を確認します。


まずは、tree をインストールします。

$ sudo apt-get -y install tree

12.1. ディレクトリ構成

$ find . -name "*.pyc" -exec rm {} \;
$ tree ~/ -L 4
/home/webapp/
├── db_backup
│   └── akiyokoproject_init.dump
├── logs
│   ├── akiyokoproject_error.log
│   ├── akiyokoproject_error_nginx.log
│   └── akiyokoproject_supervisor
└── mezzanine
    ├── akiyokoproject
    │   ├── config
    │   │   ├── __init__.py
    │   │   ├── local_settings.py
    │   │   ├── settings.py
    │   │   ├── urls.py
    │   │   └── wsgi.py
    │   ├── custom
    │   │   ├── __init__.py
    │   │   ├── static
    │   │   ├── templates
    │   │   └── templatetags
    │   ├── deploy
    │   │   ├── crontab.template
    │   │   ├── gunicorn.conf.py.template
    │   │   ├── local_settings.py.template
    │   │   ├── nginx.conf.template
    │   │   └── supervisor.conf.template
    │   ├── fabfile.py
    │   ├── gunicorn.conf.py
    │   ├── gunicorn.pid
    │   ├── gunicorn.sock
    │   ├── __init__.py
    │   ├── last.db
    │   ├── manage.py
    │   ├── requirements.txt
    │   ├── static
    │   │   ├── admin
    │   │   ├── bootstrap
    │   │   ├── CACHE
    │   │   ├── css
    │   │   ├── filebrowser
    │   │   ├── fonts
    │   │   ├── grappelli
    │   │   ├── images
    │   │   ├── img
    │   │   ├── js
    │   │   ├── media
    │   │   ├── mezzanine
    │   │   ├── plugins
    │   │   ├── robots.txt
    │   │   ├── test
    │   │   └── videos
    │   └── templates
    │       ├── base.html
    │       ├── blog
    │       ├── email
    │       ├── errors
    │       ├── generic
    │       ├── includes
    │       ├── index.html
    │       ├── pages
    │       ├── search_results.html
    │       └── twitter
    └── akiyokoproject.tar


 

12.2. 各種ログ

Nginx: /var/log/nginx

$ sudo ls -al /var/log/nginx/
total 8
drwxr-x---  2 www-data adm    4096 Jun 25 07:06 .
drwxrwxr-x 11 root     syslog 4096 Jun 25 07:06 ..
-rw-r--r--  1 root     root      0 Jun 25 07:06 access.log
-rw-r--r--  1 root     root      0 Jun 25 07:06 error.log


Supervisor: /var/log/supervisor

$ ls -al /var/log/supervisor
total 12
drwxr-xr-x  2 root root   4096 Jun 25 07:07 .
drwxrwxr-x 11 root syslog 4096 Jun 25 07:06 ..
-rw-------  1 root root      0 Jun 25 07:07 gunicorn_akiyokoproject-stderr---supervisor-VP9pQh.log
-rw-r--r--  1 root root    760 Jun 25 07:07 supervisord.log


アプリケーションログ: /var/log/akiyokoproject/

$ ls -al /var/log/akiyokoproject/
total 42520
drwxr-xr-x  2 webapp webapp     4096 Aug 11 20:11 .
drwxrwxr-x 12 root   syslog     4096 Oct 25 06:39 ..
-rw-r--r--  1 webapp webapp 43527221 Oct 25 21:30 application.log


エラー系ログ: /home/webapp/logs/

$ ls -al /home/webapp/logs/
total 3808
drwxrwxr-x 2 webapp   webapp    4096 Aug 11 20:11 .
drwxr-xr-x 8 webapp   webapp    4096 Oct 22 14:38 ..
-rw-r--r-- 1 webapp   webapp   23980 Aug 11 20:04 akiyokoproject_error.log
-rw-r--r-- 1 www-data root   3851602 Oct 25 21:58 akiyokoproject_error_nginx.log
-rw-r--r-- 1 root     root      5985 Aug 11 20:04 akiyokoproject_supervisor


 

12.3. Mezzanine の各種設定

config/local_settings.py

from __future__ import unicode_literals

SECRET_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
NEVERCACHE_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ALLOWED_HOSTS = ['akiyoko.com']

DATABASES = {
    "default": {
        # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
        "ENGINE": "django.db.backends.mysql",
        # DB name or path to database file if using sqlite3.
        "NAME": "akiyokoproject",
        # Not used with sqlite3.
        "USER": "akiyokoproject",
        # Not used with sqlite3.
        "PASSWORD": "akiyokoprojectpass",
        # Set to empty string for localhost. Not used with sqlite3.
        "HOST": "127.0.0.1",
        # Set to empty string for default. Not used with sqlite3.
        "PORT": "",
    }
}

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https")

CACHE_MIDDLEWARE_SECONDS = 60

CACHE_MIDDLEWARE_KEY_PREFIX = "akiyokoproject"

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
        "LOCATION": "127.0.0.1:11211",
    }
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"

################
# AWS SETTINGS #
################
#AWS_ACCESS_KEY_ID = "AKIxxxxxxxxxxxxxxxxx"
#AWS_SECRET_ACCESS_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

##################
# EMAIL SETTINGS #
##################
EMAIL_BACKEND = "django_ses.SESBackend"
AWS_SES_REGION_NAME = "us-west-2"
AWS_SES_REGION_ENDPOINT = "email.us-west-2.amazonaws.com"
#EMAIL_HOST_USER = "admin@akiyoko.com"
#EMAIL_USE_TLS = True
#EMAIL_HOST = "email-smtp.us-west-2.amazonaws.com"
#DEFAULT_FROM_EMAIL = SERVER_EMAIL = u"no-reply <no-reply@akiyoko.com>"

################
# LOG SETTINGS #
################
if not DEBUG:
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'file': {
                'level': 'DEBUG',
                'class': 'logging.FileHandler',
                'filename': '/var/log/akiyokoproject/application.log',
            },
        },
        'loggers': {
            'django': {
                'handlers': ['file'],
                'level': 'DEBUG',
                'propagate': True,
            },
        },
    }


 

12.4. pip でインストールしたパッケージリスト

(akiyokoproject)$ pip list
beautifulsoup4 (4.4.1)
bleach (1.4.3)
chardet (2.3.0)
Django (1.9.7)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.1)
filebrowser-safe (0.4.3)
future (0.15.2)
grappelli-safe (0.4.2)
gunicorn (19.6.0)
html5lib (0.9999999)
Mezzanine (4.1.0)
MySQL-python (1.2.5)
oauthlib (1.1.2)
Pillow (3.3.0)
pip (8.1.2)
psycopg2 (2.6.2)
python-memcached (1.58)
pytz (2016.4)
rcssmin (1.0.6)
requests (2.10.0)
requests-oauthlib (0.6.1)
rjsmin (1.0.12)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.2.2)
wheel (0.29.0)

(2016年7月時点のもの)

 

11.5. Supervisor の設定

以下の 2つの Supervisor 設定ファイルは、Fabric スクリプト実行時に自動生成されたものです。


/etc/supervisor/conf.d/akiyokoproject.conf

[program:gunicorn_akiyokoproject]
command=/home/webapp/.virtualenvs/akiyokoproject/bin/gunicorn -c gunicorn.conf.py -p gunicorn.pid config.wsgi:application
directory=/home/webapp/mezzanine/akiyokoproject
user=webapp
autostart=true
stdout_logfile = /home/webapp/logs/akiyokoproject_supervisor
autorestart=true
redirect_stderr=true
environment=LANG="en_US.UTF-8",LC_ALL="en_US.UTF-8",LC_LANG="en_US.UTF-8"


/home/webapp/mezzanine/akiyokoproject/gunicorn.conf.py

from __future__ import unicode_literals
import multiprocessing

bind = "unix:/home/webapp/mezzanine/akiyokoproject/gunicorn.sock"
workers = multiprocessing.cpu_count() * 2 + 1
errorlog = "/home/webapp/logs/akiyokoproject_error.log"
loglevel = "error"
proc_name = "akiyokoproject"


 

11.6. Nginx の設定

$ ls -al /etc/nginx/
total 80
drwxr-xr-x  6 root root 4096 Jul  8 01:03 .
drwxr-xr-x 92 root root 4096 Aug 11 21:59 ..
drwxr-xr-x  2 root root 4096 Jul 10 15:41 conf
drwxr-xr-x  2 root root 4096 Jun  3 00:16 conf.d
  (中略)
-rw-r--r--  1 root root 1601 Mar  5  2014 nginx.conf
  (中略)
drwxr-xr-x  2 root root 4096 Jul  8 00:14 sites-available
drwxr-xr-x  2 root root 4096 Jul  8 00:15 sites-enabled
  (後略)

$ ls -al /etc/nginx/conf.d/
total 8
drwxr-xr-x 2 root root 4096 Jun  2 15:16 .
drwxr-xr-x 6 root root 4096 Jun 18 04:29 ..

$ ls -al /etc/nginx/sites-available/
total 12
drwxr-xr-x 2 root root 4096 Jul  8 00:14 .
drwxr-xr-x 6 root root 4096 Jul  8 01:03 ..
-rw-r--r-- 1 root root 2593 Mar  5  2014 default

$ ls -al /etc/nginx/sites-enabled/
total 12
drwxr-xr-x 2 root   root   4096 Jun 18 04:30 .
drwxr-xr-x 6 root   root   4096 Jun 18 04:29 ..
-rw-rw-r-- 1 webapp webapp 2226 Jun 18 04:30 akiyokoproject.conf
lrwxrwxrwx 1 root   root     34 Jun 18 04:29 default -> /etc/nginx/sites-available/default


/etc/nginx/nginx.conf

user www-data;
worker_processes 4;
pid /run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # nginx-naxsi config
    ##
    # Uncomment it if you installed nginx-naxsi
    ##

    #include /etc/nginx/naxsi_core.rules;

    ##
    # nginx-passenger config
    ##
    # Uncomment it if you installed nginx-passenger
    ##

    #passenger_root /usr;
    #passenger_ruby /usr/bin/ruby;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

(後略)


/etc/nginx/sites-available/default

# You may add here your
# server {
#   ...
# }
# statements for each of your virtual hosts to this file

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /usr/share/nginx/html;
    index index.html index.htm;

    # Make site accessible from http://localhost/
    server_name localhost;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
        # Uncomment to enable naxsi on this location
        # include /etc/nginx/naxsi.rules
    }

  (中略)
}

(後略)


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

upstream akiyokoproject {
    server unix:/home/webapp/mezzanine/akiyokoproject/gunicorn.sock fail_timeout=0;
}

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;
    }

    location / {
        proxy_redirect      off;
        proxy_set_header    Host                    $host;
        proxy_set_header    X-Real-IP               $remote_addr;
        proxy_set_header    X-Forwarded-For         $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Protocol    $scheme;
        proxy_pass          http://akiyokoproject;
    }

    location /static/ {
        root            /home/webapp/mezzanine/akiyokoproject;
        access_log      off;
        log_not_found   off;
        expires 30d;
    }

    location /robots.txt {
        root            /home/webapp/mezzanine/akiyokoproject/static;
        access_log      off;
        log_not_found   off;
    }

    location /favicon.ico {
        root            /home/webapp/mezzanine/akiyokoproject/static/img;
        access_log      off;
        log_not_found   off;
    }

}

 

まとめ

今回、Mezzanine の本番デプロイとして、

  • 本番サーバ(Ubuntu)を起動
  • Fabric スクリプトを実行
  • Fabric スクリプト実行後の各種設定
    • SSL の設定
    • URL の転送設定
    • BASIC認証の設定
    • MySQL のインストール
    • メールの設定
    • ログの設定
    • タイムゾーンを Asia/Tokyo に変更
    • sitemap.xml / robot.txt の設定

を実施した内容を記載しました。

次回は、Mezzanine 本番設定の第四弾として、「その4:Mezzanine の運用設定」について記載します。


 

参考本

CentOS の本ならたくさんあるのに、Ubuntu の本はあまり無いんですよねぇ。
上級者向けではないですが、こんな感じでしょうか。

絶対つまずかない Linuxサーバー構築ガイド(日経BPパソコンベストムック)

絶対つまずかない Linuxサーバー構築ガイド(日経BPパソコンベストムック)


MySQL 本なら。

MySQL徹底入門 第3版 ~5.5新機能対応~

MySQL徹底入門 第3版 ~5.5新機能対応~

  • 作者: 遠藤俊裕,坂井恵,館山聖司,鶴長鎮一,とみたまさひろ,班石悦夫,松信嘉範
  • 出版社/メーカー: 翔泳社
  • 発売日: 2011/08/26
  • メディア: 大型本
  • 購入: 9人 クリック: 82回
  • この商品を含むブログ (9件) を見る

*1:Mezzanine を採用したのは、個人の趣味です。

*2:Fabric スクリプトの実体は、https://github.com/stephenmcd/mezzanine/blob/4.1.0/mezzanine/project_template/fabfile.py です。

*3:参考:PostgreSQLとMySQLはどちらかに明確な優位性がありますか? - QA@IT

*4:Fabric じゃないけど一応

*5:現時点の最新は 4.2.2 ですが、2016年7月当時は 4.1.0 でした。

*6:実際に運用しているドメインは akiyoko.com ではありませんので悪しからず。

*7:「完全な管理者アクセス権限(AdministratorAccess)」ポリシーを付けた EC2インスタンス用 Role です。

*8:ちょっとした裏技(?)ですが、ubuntu ユーザの authorized_keys ファイルをコピーすれば簡単です。

*9:Git 管理下に置きたくない各種設定は、local_settings.py で管理します。

*10:Mezzanine 4.2.0 から修正されたようです。 https://github.com/stephenmcd/mezzanine/commit/898e330a26500d473663f9602165e40d313677d1

*11:「GoGetSSL」からダウンロードした中間証明書に「^M」が入ってたけど、気にしなくて OK!

*12:秘密鍵は GoGetSSL からメールで送られます。

*13:「ec2-prod」ロールを EC2 に付与しているため、「AWS_ACCESS_KEY_ID」および「AWS_SECRET_ACCESS_KEY」が不要になっています。もし IAMロールをインスタンス起動時に付与しない場合は、6つが最低限必要となります。

*14:以下のように Warning がいくつか出ましたが、特に影響なさそうなので無視しました。