Python
Django
python3
デプロイ
Django2.0

DjangoをVPSにデプロイする手順を一からわかりやすく丁寧に

はじめに

この投稿は、Python製フルスタックフレームワークであるDjangoをVPSにデプロイする際の手順を出来る限りわかりやすく丁寧に解説した投稿です。実際に私がインスタンスを一つ立てて、最初からデプロイ完了するまでをまとめました。
今回使ったVPSは、WEB ArenaというVPSです。VPSというとまず、さくら。次にConoHaが多いようですので、WEB Aranaユーザーの方の参考にもなれば、と思って書いております。逆に、DjangoをさくらやConoHaにデプロイしたい方も"4. SSH接続"からご覧頂ければ、かなり似通った部分があるので、参考になるかと思います。AWSのEC2をお使いの方も参考になると思います。

1. インスタンスを立てる

WEB ArenaのHPの赤枠部分から、VPSクラウドを選択します。
9c8f1a1577da9f1fa60235879933c702-png.jpg

赤枠部分から申し込み手続きに入ります。
937e604d92b186bdbe21f7dfaf40d832.jpg

申込み方法は、

  • オンライン申込み

  • 書面での申込み

の2つがあります。今回は、オンライン申込みで、支払い方法をクレジット決済とします。赤枠部分をクリックします。
a612e7a5599beb7bfa57715269968e25.jpg

  • 赤枠部分① インスタンスタイプが列挙されています。左から右にいくに従って、スペックが上がっていきます。たまに在庫状況が悪い(最悪在庫なし=申し込み不可)のときもあるので、チェックしましょう。

  • 赤枠部分② 実際にインスタンスタイプを選択します。

  • 赤枠部分③ オプションとして、バックアップ機能とロードバランサー機能があります。必要に応じて選択しましょう。

  • 赤枠部分④ サービス申込みのための各種情報(パスワード、メールアドレス等)を入力します。

画面最下部「次へ」から次のページへ進みます。
f4174a610b4af36cc5a310817629bf69.jpg

  • 赤枠部分① すでに契約者IDを持っている(=WEB Arenaを利用したことがある)のであれば、一つの契約者IDに支払いをまとめることが可能ですが、おそらくこの投稿をご覧の方は該当しないでしょう。「新しい契約者IDを作成する」を選択します。
  • 赤枠部分② 利用規約に同意することが必要です。利用規約を一通り読んで問題なければチェックを入れて、画面最下部「次へ」から次のページへ進みます。

WebARENA_VPSクラウド.jpg

これ以降は画像での説明はしませんが、支払い方法を選択して、今回はクレジットカードですのでクレジットカード情報を入力し、契約者名・住所・電話番号・メールアドレス等を入力します。最後に申込内容の確認して、申込みを正式にします。これで、申し込みは完了です。

2. インスタンスの初期設定

次に、サーバ管理用のコントロールパネル(コンパネ)にアクセスして、立てたインスタンスの初期設定を行います。

サーバ管理用コンパネ

  • 赤枠部分① ユーザー(サービスID)欄にサービスIDを入力します。サービスIDは、VPS申込み完了後に届くメールに記載があります。

  • 赤枠部分② パスワード欄にパスワードを入力します。このパスワードはVPS申込時にご自分で設定されたパスワードを入力します。

サービスIDとパスワードを入力後、Loginボタンをクリックします。すると、以下のような画面になります。ここで、赤枠部分のキーペアをクリックします。SSHで接続するために必要なキーペアの生成に入ります。

WebARENA.jpg

赤枠部分「キーペアを生成」をクリックします。

WebARENA.jpg

  • 矢印① キーペアの名前をつけてください(必須)。ここでは、qiitaという名前にします。

  • 矢印② キーペアの説明をつけることが可能です。後ほど、見てすぐに何のキーペアなのかわかるようにしましょう(任意)。ここでは、qiita投稿用という説明をつけておきます。

  • 矢印③ ここは空白のままです。

ここまで来たら、作成ボタンをクリックします。
※作成ボタンをクリックすると、キーが自動的にダウンロードされます。SSH接続で必要になると同時に、再ダウンロードができないため、注意して扱いましょう。

WebARENA.jpg

画面が自動的に元に戻ります。赤枠の通りキーペアが作成されていることを確認します。

WebARENA.jpg

次に、赤枠部分のセキュリティグループをクリックします。

WebARENA.jpg

赤枠部分のセキュリティグループを作成ボタンをクリックします。

WebARENA.jpg

  • 矢印① セキュリティグループ名をつけます(必須)。ここでもqiitaとしましょう。

  • 矢印② 説明をつけられるところもキーペア作成場面と同じです。

  • 矢印③ こちらで、ポート開放などの設定を行います。間違って設定を行うとセキュリティホールになりかねませんので、しっかりと理解した上で設定をしましょう。

すべて入力が終われば、作成ボタンをクリックします。

WebARENA.jpg

画面が再び自動的に元に戻ります。赤枠の通りセキュリティグループが作成されていることを確認します。

WebARENA.jpg

3. インスタンスの起動

いよいよ、インスタンスを起動してみます。赤枠部分のマシンイメージを選択します。

WebARENA.jpg

  • 矢印① イメージが複数用意されているので、用途によって選びましょう。ここでは、512M-SSDタイプ(KVM)を選択します。

  • 矢印② インスタンス起動をクリックします。

WebARENA.jpg

  • 矢印① インスタンスの名前をつけましょう。

  • 矢印② さきほど作成したキーペアが選択されていることを確認します。もし選択されていなければ、プルダウンメニューをクリックして選択します。

  • 矢印③ セキュリティグループを選択します。同じくさきほど作成したセキュリティグループを左側の枠で選択し、 > ボタンをクリックします。すると、選択したセキュリティグループが、右側の枠に移動します。移動確認後、起動ボタンをクリックします。

WebARENA.jpg

その後、元の画面に戻るので、赤枠部分のインスタンスをクリック。

WebARENA.jpg

立てたインスタンス(今回はインスタンス名がqiita)の状態(矢印a)がrunningになっていれば、インスタンスの起動は完了しています。
また、IPアドレス(矢印b)をこの次で使うので、メモしておきます。

WebARENA.jpg

4.SSHで接続

ここからは、SSHでインスタンスに接続していきます。他のVPS(さくら、ConoHa等)やAWSのEC2をお使いの方も、概ね同じ流れかと思われます。また、クライアントからの作業となります。この投稿で使ったクライアントはmacです。従いまして、macでの作業を前提とします。

まず、ターミナルを立ち上げます。

キーペアをダウンロードしたディレクトリ(※大抵は~/downloadsです)へ移動。

$ cd ~/downloads

仮にキーペアのファイル名がhoge.pemだという前提で今後の作業を続けていきます。mvコマンドで、hoge.pemファイルを、~/.sshに移動します。
※もし.sshディレクトリがなければ、mkdirコマンドであらかじめ作成しておきましょう。

$ mv hoge.pem ~/.ssh

~/.sshにカレントディレクトリを移動します

$ cd ~/.ssh

ssh接続をします。IPアドレスは、メモしたIPアドレスです。
コマンド書式: ssh ユーザー名@IPアドレス -i キーファイル

$ ssh root@123.456.789 -i hoge.pem

初回接続は、以下のメッセージが出ることがあります。ここは、yesと入力してエンターです。

# The authenticity of host '123.456.789 (123.456.789)' can't be established.
# ECDSA key fingerprint is SHA256:(※ここには、ランダムの英数字が入ります).
# Are you sure you want to continue connecting (yes/no)?
yes

※ここで、Permission diniedとエラーが出た場合は、キーペアのパーミッションを変更する必要があるので、以下のコマンドを入力します。それから、もう一度試してみてください。

$ chmod 400 hoge.pem

作業用ユーザの作成をします。rootでの作業はセキュリティ上望ましくありません。追加するユーザ名はqiitaとします。なお、ここで出てくる'#'で始まるコマンドは、rootでコマンド実行をしていることを表します。コメントアウトではありません。

# useradd qiita
# passwd qiita
# ユーザqiitaのパスワードを入力

作成したユーザをwheelグループに追加します。

# usermod -G wheel qiita

次にユーザにsudoコマンドを許可します。

# visudo

ここで、sudoersファイルを編集することになります。この中で、

sudoers
(中略)
## Same thing without a password
# %wheel  ALL=(ALL)       NOPASSWD: ALL
(中略)

こちらの部分のコメントアウトを外します。すわなち該当部分を以下のような状態にしましょう。

# Same thing without a password
%wheel  ALL=(ALL)       NOPASSWD: ALL

次のステップで使うディレクトリを作成し、パーミッションも変更しておきます。

# mkdir /home/qiita/.ssh
# chmod 700 /home/qiita/.ssh

これで、qiitaでログインをすると、sudoコマンド実行が出来るようになりました。
次に、クライアントからユーザqiitaでssh接続が出来るようにします。まずは、一旦ssh接続を切り、ローカル上で公開鍵と秘密鍵をssh-keygenコマンドで生成します。

$ ssh-keygen

実行すると、以下のようになります。

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/ユーザー名/.ssh/id_rsa): 
    ⇢ 作成した鍵を設置するディレクトリの確認です。エンターキーを叩いてください。
Enter passphrase (empty for no passphrase): 
    ⇢  パスフレーズを入力します。
Enter same passphrase again: 
    ⇢ 同じパスフレーズを入力しましょう。
Your identification has been saved in /Users/ユーザ名/.ssh/id_rsa.
Your public key has been saved in /Users/ユーザ名/.ssh/id_rsa.pub.
(中略)

作成された公開鍵を、VPS側にcpsコマンドで移動します。コマンドが長いので、書式の解説をします。

$ scp -i hoge.pem ~/.ssh/id_rsa.pub root@123.456.789:/home/qiita/.ssh
  • scp(※コマンド)
  • iオプション(※以下に続くキーペアでscpコマンド実行時の承認を行う)
  • hoge.pem(※承認に用いるキーペア)
  • ~/.ssh/id_rsa_.pub(※ホームディレクトリ上にある.sshディレクトリ配下のid_sra.pubというファイルを移動する) 
  • root(※サーバ承認に用いるユーザにrootを指定する)
  • @123.456.789(※scpコマンドを実行するサーバのIPアドレス)
  • :home/qiita/.ssh(※homeディレクトリのqiitaディレクトリ配下の.sshディレクトリにファイルを移動する)

さて、公開鍵の移動が完了したら、移動した公開鍵をVPSのauthorized_keysファイルに登録します。ローカルからVPSにrootユーザでssh接続した上で、以下のコマンドを実行します。

# cd /home/qiita/.ssh
# cat id_rsa.pub >>  authorized_keys
# chmod 600 authorized_keys

また、ssh接続する際に、sshの設定ファイルの編集が必要になります。

# vi /etc/ssh/sshd_config

編集点は以下です。

  • 今後のrootログインが出来ないようにします。
    PermitRootLogin no

  • パスワードなしの状態でのログインが出来ないようにします。
    PermitEmptyPasswords no

  • 鍵認証が出来るようにします。
    PubkeyAuthentication yes

  • パスワード認証が出来ないようにします。
    PasswordAuthentication no

編集後は、以下のコマンドでsshデーモンの再起動をかけます。

# systemctl restart sshd

最後にこのままだと以下のファイルの所有者の関係で接続が出来ないので、所有者変更をします。

# chown qiita:qiita /home/qiita/.ssh

ここまでで、ローカルからVPSにユーザqiitaで認証にid_rsaを用いてssh接続できるようになりました。早速接続してみましょう。一旦VPSとの接続をexitコマンドで切り、以下のコマンドを実行します。

$ cd ~/.ssh
$ ssh -i id_rsa qiita@123.456.789

パスフレーズを求められたら、鍵生成時に設定したパスフレーズを入力しましょう。接続が出来るはずです。

5. ポートの設定

※これからは、ユーザqiitaで作業をしていきます。また、サーバのディストリビューションはCentOS 7.4であることを前提とします。

最初に、ファイアウォールの設定です。以下のコマンドを実行します。

# ファイアウォールのデーモンの起動
$ sudo systemctl start firewalld

# ファイアウォールデーモンの有効化
$ sudo systemctl enabale firewalld

# ファイアウォールデーモンの状態確認
$ sudo systemctl status firewalld

ズラズラと、文字列が出てきますが、上の方にActive: active (running) と出ていれば、設定完了です。なお、SELinuxの設定も行うと良いでしょう。
※SELinuxの設定を有効化をするか否かというのは、議論があります。このことについては、この投稿から外れてしまうので、触れません。
http通信のためのポート開放も行います。必要に応じて、httpsのポートも開放しましょう。最後に、ファイアウォールデーモンの再起動をかけます。

$ sudo firewall-cmd --permanent --add-service=http
$ sudo systemctl restart firewalld

以上の作業で、サーバの設定をすることが出来ました。

6. Pythonの環境構築

いよいよ本番、環境構築です。まずは、yumのアップデートをします。

$ sudo yum update

yumには、現時点(2018/08/10)ではPython 3.7が含まれていませんので、Pyenvを用いてPython 3.7をインストールします。Pyenv自体もインストール方法がいくつかありますが、ここではPyenv Installerでインストールをします。そのためには、Gitが必要になります。Gitをインストールしてからスタートです。

$ sudo yum install git
$ curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

viで、~/にある.bash_profileにパスを通します。viは操作が少々特殊ですが、こちらの投稿をご覧の方であれば、操作方法はご存知でしょうので、割愛します。

export PATH="/home/qiita/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

以上を追記します。追記後は、以下のコマンドを入力します。再読み込みを意味します。

$ source .bash_profile

これでPyenvが動作します。早速Python 3.7をインストールしたいところですが、インストールに必要なライブラリを事前にインストールする必要があります。yumでインストールしましょう。

$ sudo yum install gcc zlib-devel libffi-devel openssl-devel readline-devel sqlite-devel bzip2-devel

Python 3.7のインストールをします(正確には3.7.0)。

pyenv install 3.7.0

ちゃんとインストールされていることを確認します。

python -V
# Python 3.7.0

7. Djangoの環境構築

次に、Djangoのインストールに入ります。Djangoはライブラリです。ライブラリの管理ツールとして、大抵はPipを使います。加えてここでは、Pipenvを使ってみましょう。Pipenvは、ライブラリの管理ツールとしてのPipと、仮想環境(一つ一つ環境を作ることによって、システムを汚さないで済む)の管理ツールとしてのVirtualenv(現在は、Venvという名称になっています)を一緒にしたもの、と考えるとわかりやすいです。

作業に入りましょう。mysiteディレクトリを作成し、そのディレクトリへ移動します。

$ mkdir mysite
$ cd mysite

Pip自体のアップグレードを最初に行います。そして、PipでPipenvをインストールします。

$ pip install --upgrade pip
$ pip install pipenv

これでPipenvを使うことが出来ます。PipfileとPipfile.lockを生成して仮想環境構築のために以下のコマンドを実行します。

$ pipenv lock

次に、仮想環境の内部に入ります。以下のコマンドを実行します。

$ pipenv shell

この状態で、必要なライブラリをインストールします。Djangoをインストールするので、以下のコマンドを実行します。依存ライブラリ(今回はpytz)も同時にインストールされます。

$ pipenv install django

この状態で、プロジェクト(ここでの名前はqiitaとします)を作成します。以下のコマンドを実行します。

$ django-admin startproject qiita

qiitaというディレクトリにプロジェクトが作成されますので、移動して、開発サーバが起動するかどうか確認し、この開発サーバは本番環境用ではないので、すぐにシャットダウンします。

$ cd qiita
$ python manage.py runserver

migrationを適用していない、という注意が出ますが、現時点では問題ありません。それ以外は問題なさそうであれば、Control + Cでシャットダウンします。また、セキュリティ上、settings.pyの編集を行います。変更点は以下です。

  • DEBUGをTrueからFalseに修正します。
  • SECRET_KEYを環境変数に修正します。
  • ALLOWED_HOSTSに、サーバのIPアドレスを設定します。

このうち、わかりづらいであろう、 SECRET_KEYの修正を解説します。まず、settings.pyがあるディレクトリに移動し、settigns.pyを編集します。

$ cd qiita
$ vi settings.py

SECRET_KEY部分をあらかじめコピーしておきます。例えば、以下のようにSECRET_KEY部分がなっているとします。

SECRET_KEY = 'hoge'

この場合は、hogeをコピーしておきます。つまり、``は必要ありません。コピー後は、以下のように編集します。

SECRET_KEY = os.environ['SECRET_KEY']

次に、~/にある.bash_profileに以下の内容を追記しましょう。追記後は、sourceコマンドで、再読み込みをします。

$ vi ~/.bash_profile
# 以下を追記
export SECRET_KEY=hoge
$ source ~/.bash_profile

これでDjangoの環境構築も完了です。しかし、これではサーバ周りが本番環境とはまだ言えません。そのために、WEBサーバのnginxとDjangoをつなぐGunicornをインストールします。

8. nginxとGuicornのインストール

こちらの投稿もそろそろ終盤に差し掛かってきました。WEBサーバのnginxをインストールします。以下のコマンドでnginxをインストールし起動しましょう。

$ sudo yum install nginx
$ sudo nginx

以前メモしたIPアドレスをブラウザで開いてみてください。以下の画面になるはずです。

Test_Page_for_the_Nginx_HTTP_Server_on_Fedora.jpg

次にGunicornをインストールします。

$ pipenv install gunicorn

manage.pyのあるディレクトリにて、テストで起動しましょう。エラーが出ずに起動できるはずです。起動を確認したら、一旦Control + Cでシャットダウンします。

$ cd ..
$ gunicorn qiita.wsgi --bind=0.0.0.0:8000

Gnicornとnginxとつなぐ(リバースプロキシと言います)ための設定をnginxの設定ファイルに書き込みます。

$ cd /etc/nginx

# 設定ファイルは、コピーを取っておきましょう。
$ sudo cp nginx.conf nginx_old.conf

# viで書き込みます。
$ sudo vi nginx.conf

こちらの設定ファイルは、長いので、以下をお使いください。

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

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

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

        upstream app_server {
                server 127.0.0.1:8000 fail_timeout=0;
        }
    server {
#        listen       80 default_server;
#        listen       [::]:80 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;

        listen 80;
        server_name ここにサーバのIPアドレスを設定します。;
        client_max_body_size 4G;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://app_server;
        }
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2 default_server;
#        listen       [::]:443 ssl http2 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers HIGH:!aNULL:!MD5;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        location / {
#        }
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

}

SELinuxの設定を変更しておきます。以下のコマンドを実行します。

$ setsebool -P httpd_can_network_connect 1

ここまでの設定を反映させるために、nginxの再起動をかけます。先ほどと同じく、namage.pyのあるディレクトリでGnuicornも起動しておきましょう。

$ sudo systemctl restart nginx
$ gunicorn qiita.wsgi --bind=0.0.0.0:8000

いよいよ、先ほどのIPアドレスを開きましょう。以下の画面が出ましたか?

Django__the_Web_framework_for_perfectionists_with_deadlines_.jpg

細かい設定はまだありますが、以上でデプロイに関する基礎的な内容はだいたいお伝えできたかと思います。丁寧な解説を試みた結果、かなり長くなってしまいましたが、お役に立てれば幸いです。