『入る学科間違えた高専生』の日記

プログラミングのコードを書いたりする予定です。あとは日記等。あといつまで高専生やねん

Ubuntu + Nginx + Gunicornを手動デプロイする手順

はじめに

なんか、久しぶりにド素人的なタイトル。(いつもとか言わないでくださいw
今回は JUN (id:JUN_NETWORKS) から何か書いてくれって言われたのでこれです。
本来はNginxの話(またどうでもいいクソ記事)を書くつもりだったんですが、組み合わせのデプロイですね。

環境

新たにUbuntu環境を整えるのがアレだったので今回は久しぶりにVagrant

Vagrantのインストールは過去記事を参照ください
今回はVagrantが重要ではないので。

nnsnodnb.hatenablog.jp

セットアップ

一応、サラッとセットアップだけ

A list of base boxes for Vagrant - Vagrantbox.es

今回もここからいただきました。

$ vagrant box add xenial https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-vagrant.box
$ vagrant init xenial

んであとは、下のように Vagrantfile をお好きなように変更してくださいな。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "xenial"

  config.vm.network "forwarded_port", guest: 80, host: 80, host_ip: "127.0.0.1"

  config.vm.network "private_network", ip: "192.168.33.10"
end

今回は、ホストOSとゲストOSともに80番ポートを使用するのでホストOS側で80番を使用している場合は止めるか、違うポート番号を指定しましょう!
今は知りませんがWebサーバを立ち上げてないのになぜか知りませんが、 vagrant up できない方はSkypeを確かめてください。あいつは80番ポートを使っていることがあります。

$ vagrant up
# 立ち上がったら
$ vagrant ssh

やっていくぞー

Vagrantvagrant up の時に Vagrantfile に以下のように記述があればシェルスクリプトを実行してくれますが、今回はそれなしでいきますー

・・・
  config.vm.provision 'shell', path: 'provision.sh'
end

手始め

$ sudo apt-get update && sudo apt-get upgrade -y

とりあえずパッケージアップデートしておきましょう。基本ですね。

Nginxインストール

$ curl -sSL http://nginx.org/keys/nginx_signing.key | sudo apt-key add -

/etc/apt/sources.list を vi 等で以下を追加

deb http://nginx.org/packages/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/ubuntu/ xenial nginx
$ sudo apt-get update
$ sudo apt-get install nginx -y
$ nginx -v
nginx version: nginx/1.12.2
$ sudo update-rc.d nginx defaults
$ sudo /etc/init.d/nginx start

とりあえず http://192.168.33.10/ にアクセスして Welcome to nginx! が表示されることを確認。

f:id:nanashinodonbee:20180224090134p:plain

とりあえず大丈夫そうね。

Python 3.6.3のインストール

いつもだったら PyEnv 入れて楽しますが、せっかくなのでリポジトリから

$ sudo apt-get install apt-file -y
$ sudo apt-get update
$ sudo apt-get install software-properties-common -y
$ sudo add-apt-repository ppa:jonathonf/python-3.6  #  途中で `Press [ENTER] to continue or ctrl-c to cancel adding it` と出るのでエンター
$ sudo apt-get update
$ sudo apt-get install python3.6 python3.6-dev -y
$ wget https://bootstrap.pypa.io/get-pip.py
$ sudo python3.6 get-pip.py
$ python3.6 -V
Python 3.6.3
$ pip3.6 -V
pip 9.0.1 from /usr/local/lib/python3.6/dist-packages (python 3.6)

Djangoのインストール

今回もちょっと面倒っちぃので Virtualenv 入れておきます。そしたらaliasしなくても python とか pip でアクセスできるし環境汚れないので

$ sudo pip3.6 install virtualenv
$ virtualenv xenial_django
Using base prefix '/usr'
New python executable in /home/vagrant/xenial_django/bin/python3.6
Also creating executable in /home/vagrant/xenial_django/bin/python
Installing setuptools, pip, wheel...done.
$ cd xenial_django && . bin/activate

これで、簡単に操作できるようになったわけだ。
なんか最近Qiitaとかに Djangoをインストールして動かすまで とかの記事が増えてきてオコですが。今回もそれしますw

$ pip install django==2.0.2  #  今回はバージョン指定で。環境を先述してるので
$ django-admin startproject xenial_django .  #  ここにプロジェクト作成
$ python manage.py migrate
$ python manage.py runserver 0.0.0.0:8000

このままだと、一応アクセスはできるんですが、 DisallowedHost のエラーが出てしまうので xenial_django/settings.pyALLOWED_HOSTS192.168.33.10 を追加しておきましょう。

f:id:nanashinodonbee:20180224092044p:plain

今回は、デプロイのそれだけなので django-admin startapp hoge とかはしません。面倒です!

それとついでにここで gunicorn もインストールしておきましょう

$ pip install gunicorn
Collecting gunicorn
  Downloading gunicorn-19.7.1-py2.py3-none-any.whl (111kB)
    100% |████████████████████████████████| 112kB 4.2MB/s
Installing collected packages: gunicorn
Successfully installed gunicorn-19.7.1

バージョン 19.71 って一体どうなってんだ。って感じだ。

デプロイやっていこう

  • gunicorn の設定ファイルを書いたほうが圧倒的に楽なのでしよう
$ touch gunicorn_conf.py
import multiprocessing
import os.path

bind = 'unix://gunicorn.sock'
daemon = True
pidfile = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'gunicorn.pid')
reload = True

大体自分はいつもこんな感じで、たまにWorkerを増やしたり

workers = multiprocessing.cpu_count() + 2

Workerに関しては適宜増やす感じで。

・・・
DEBUG = False
・・・
STATIC_ROOT = 'staticfiles/'
$ python manage.py collectstatic
  • Nginxの設定ファイルをいじろう

/etc/nginx/conf.d/default.conf が元のファイルなのでコピーするなりなんなり。しなくてもいいならそれでもいい。任せます。

upstream xenial_django {
    server unix:/home/vagrant/xenial_django/gunicorn.sock;
}

server {
    listen 80;
    server_name  localhost;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    access_log /home/vagrant/xenial_django/access.log main;

    location / {
        root /home/vagrant/xenial_django;
        proxy_pass http://xenial_django;
    }

    location /static {
        alias /home/vagrant/xenial_django/staticfiles;
    }
}

大体こんな感じでいけるかなー。 upstream xenial_djangoxenial_django は自分がわかればなんでもいいです。今回はとりあえずプロジェクト名にしてみました。

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

一応、エラーのないかどうかの確認だけはしましょう。大丈夫そうであれば

$ sudo /etc/init.d/nginx restart
  • gunicorn 起動
$ gunicorn xenial_django.wsgi:application -c gunicorn_conf.py

http://192.168.33.10 にアクセスして Not Found ページが出たら成功。

f:id:nanashinodonbee:20180224094522p:plain

staticfiles にstaticファイルを書き出しているのでちゃんと表示されているようです。

f:id:nanashinodonbee:20180224094723p:plain

nginxの設定ファイルで access.log の設定したので、それも確認しておきます。

$ tail -f /home/vagrant/xenial_django/access.log
192.168.33.1 - - [24/Feb/2018:00:46:37 +0000] "GET / HTTP/1.1" 404 74 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /admin HTTP/1.1" 301 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /admin/ HTTP/1.1" 302 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /admin/login/?next=/admin/ HTTP/1.1" 200 1855 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /static/admin/css/base.css HTTP/1.1" 200 16106 "http://192.168.33.10/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /static/admin/css/login.css HTTP/1.1" 200 1203 "http://192.168.33.10/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /static/admin/css/responsive.css HTTP/1.1" 200 17894 "http://192.168.33.10/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:38 +0000] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423 "http://192.168.33.10/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:39 +0000] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 80304 "http://192.168.33.10/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"
192.168.33.1 - - [24/Feb/2018:00:46:39 +0000] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 81348 "http://192.168.33.10/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6" "-"

上記は、 http://192.168.33.10http://192.168.33.10/admin にアクセスした結果です。ちゃんと全てに対し正常に動いているようです。

おまけ

本当は、今回は海外IPを全部弾くやつをアレするアレな記事を書く予定だったのでそれも一応やります。

#!/usr/bin/env python3.6

import gzip
import os
import re
import urllib.request

DOWNLOAD_FILE = 'cidr.txt.gz'

urllib.request.urlretrieve(
        'http://nami.jp/ipv4bycc/cidr.txt.gz',
        DOWNLOAD_FILE
)

with gzip.open(DOWNLOAD_FILE, 'rt') as f, open('./deny_ip.conf', 'w') as writer:
    match = re.compile('\w+\t')
    for line in f:
        text = line.replace('\n', '')
        if text.startswith('JP'):
            continue
        group = match.search(text)
        text = 'deny ' + text.replace(group[0], '') + ';\n'

        writer.write(text)

os.remove(DOWNLOAD_FILE)

全部標準ライブラリで書いてるので、この環境であれば動きます。

$ curl -sS https://gist.githubusercontent.com/nnsnodnb/a07c4c5104e11242e74d04e5d155b2e6/raw/136fd667bdd1be38b8af709097ea674ed26197f4/deny.py | python3.6

これで書き出された、 deny_ip.conf を先程の nginx の設定ファイルにカキコします。

server {
・・・
    include /path/to/deny_ip.conf;
}

これであとはnginxを restart or reload で反映されるはずです。

最後に

はい。ちゃんと動きましたでしょうか?
uWSGIgunicorn の差については今回は触れません。場合によっては、前者、はたまた後者とかあります。自前のアプリケーションに沿った物を選びましょう。

そこら辺は以下記事かな。

blog.appdynamics.com

$ vagrant destroy

お後がよろしいようで。