Tenn25 Blog
Mezzanineでブログ作成 1.サーバ構築
27.10.201813 Min Read — In others

プラグインを入れて Markdown で投稿できるようになった!!(これも後々記事にする)
今回は、このブログを立てるにあたってやったことをまとめるよ。

概要

今回は CentOS7 を使っています。
Amazon Linux2 を使ったら extra でのインストール苦しんだので諦めました。

  • Web サーバ(Nginx)
  • アプリケーションサーバ(Gunicorn)
  • DB サーバ(Postgresql)
    のインストールを行う。

つまづくポイントが多いので、段階を踏んで確認していきます。

  • Python 内蔵のサーバで動作確認
  • Gunicorn を起動して動作確認
  • Nginx をリバースプロキシとしてドメインからアクセスして確認

ここまで問題なければ、次回の記事で Python 製 CMS の Mezzanine を入れて再度 0 からプロジェクトを作成し直します。


1.Nginx のインストール

$ vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1

$ yum install nginx

$ nginx -v
nginx version: nginx/1.15.5

$ sudo systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

2. Postgresql11 のインストール

実は Postgres をちゃんと使うのは初めて。せっかくだから新しい 11 を入れる。 参考: https://soudai.hatenablog.com/entry/2018/10/08/023918

$ sudo yum update
$ sudo yum localinstall https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-centos11-11-2.noarch.rpm
$ sudo su postgres
bash-4.2$ /usr/pgsql-11/bin/initdb -E UTF-8 --no-locale -D /var/lib/pgsql/11/data
bash-4.2$ exit

$ sudo systemctl start postgresql-11.service
$ sudo systemctl enable postgresql-11.service
Created symlink from /etc/systemd/system/multi-user.target.wants/postgresql-11.service to /usr/lib/systemd/system/postgresql-11.service.

$ psql -V
psql (PostgreSQL) 11.0
  • psql コマンドで DB に接続
$ sudo passwd postgres
$ su postgres
パスワード:

bash-4.2$ cd
bash-4.2$ pwd
/var/lib/pgsql
bash-4.2$ psql
  • DB 内の設定

注意: DB 名を大文字にしたけど、小文字で登録されたみたい。( ポスグレは大文字小文字が区別される??)

postgres=# CREATE DATABASE [DB名] LC_COLLATE 'C' LC_CTYPE 'C' ENCODING 'UTF8' TEMPLATE template0;
CREATE DATABASE
postgres=# CREATE USER [ユーザ名] WITH PASSWORD '******';
CREATE ROLE
postgres=# ALTER ROLE [ユーザ名] SET client_encoding TO 'utf8';
ALTER ROLE
postgres=# ALTER ROLE [ユーザ名] SET default_transaction_isolation TO 'read committed';
ALTER ROLE
postgres=# ALTER ROLE [ユーザ名] SET timezone TO 'UTC+9';
ALTER ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE BLOG TO [ユーザ名];
GRANT
postgres=# \q
bash-4.2$

3. Python のインストール

参考: https://qiita.com/tinaba/items/01bc72c100f97438a36e

  • まずは Python3 系と pip をインストール
$ sudo yum install -y https://centos7.iuscommunity.org/ius-release.rpm
$ sudo yum install -y python36u python36u-libs python36u-devel python36u-pip psycopg2-binary
$ python3.6 -V
Python 3.6.5

$ pip3.6 -V
pip 9.0.1 from /usr/lib/python3.6/site-packages (python 3.6)
  • Python3.6 の仮想環境を作る

今回、ブログ関係のファイルは/opt/blog/に配置します。
また、CentOS7 自体には python2 系のパスが通ってしまっているため、
venv で Python3 系の仮想環境を作って、その上で Django のプロジェクトを動かします。

[tenn25@ip-10-0-0-51 blog]$ pwd
/opt/blog
[tenn25@ip-10-0-0-51 blog]$ sudo python3.6 -m venv py36
[tenn25@ip-10-0-0-51 blog]$ ll
合計 0
drwxr-xr-x. 5 root root 74 10月 21 21:01 py36

[tenn25@ip-10-0-0-51 ~]$ source py36/bin/activate
(py36) [tenn25@ip-10-0-0-51 ~]$

(py36) [tenn25@ip-10-0-0-51 ~]$ sudo chown tenn25:tenn25 py36 -R
(py36) [tenn25@ip-10-0-0-51 ~]$ ll
合計 0
drwxr-xr-x. 5 tenn25 tenn25 74 10月 21 01:51 py36

(py36) [tenn25@ip-10-0-0-51 ~]$ pip install django gunicorn psycopg2 Pillow
Installing collected packages: pytz, django, gunicorn, psycopg2, Pillow
Successfully installed Pillow-5.3.0 django-2.1.2 gunicorn-19.9.0 psycopg2-2.7.5 pytz-2018.5
  • Django プロジェクトを作成

インストールができたので、Django のプロジェクトを作成します。
コンソールの左に(仮想環境名)が表示されてる状態で行います。
仮想環境の起動は source [インストールパス]/py36/bin/activate
仮想環境の停止は deactivate です。

(py36) [tenn25@ip-10-0-0-51 ~]$ django-admin startproject blog
(py36) [tenn25@ip-10-0-0-51 ~]$ ll
合計 0
drwxrwxr-x. 3 tenn25 tenn25  35 10月 21 02:03 blog
drwxr-xr-x. 5 tenn25 tenn25 100 10月 21 01:55 py36
  • プロジェクトファイルを編集

プロジェクトルートの名前も blog にしてしまったせいで
階層が非常にわかりづらくなってしまった。。

/opt/blog は自分で作成した作業ディレクトリです。
その下に Django プロジェクト[blog]を作成しました。

(py36) [tenn25@ip-10-0-0-51 blog]$ pwd
/opt/blog
(py36) [tenn25@ip-10-0-0-51 blog]$ sudo vi blog/blog/settings.py
  • 以下の 3 項目を編集

今回は Postgresql を使うので ENGINE は以下のように記載する。

ALLOWED_HOSTS = ['tenn25.com']
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'

DATABASES = {
    'default': {
        #'ENGINE': 'django.db.backends.sqlite3',
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        #'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'NAME': 'blog',
        'USER': 'tenn25',
        'PASSWORD': '**********',
        'HOST': 'localhost',
        'PORT': '',
    }
}
  • マイグレーションを実施

アプリケーションで使うデータベースの定義を自動的に作成して DB に反映する機能を migration と言うらしい。
makemigration→migrate の順に行う。

(py36) [tenn25@ip-10-0-0-51 blog]$ pwd
/opt/blog/blog
(py36) [tenn25@ip-10-0-0-51 blog]$ ls
blog  manage.py

(py36) [tenn25@ip-10-0-0-51 blog]$ python manage.py makemigrations
/opt/blog/py36/lib64/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
  """)
No changes detected
  • エラーが出たので、[psycopg2-binary]をインストール。
(py36) [tenn25@ip-10-0-0-51 blog]$ pip install psycopg2-binary
  • 再度マイグレート準備
(py36) [tenn25@ip-10-0-0-51 blog]$ python manage.py makemigrations
No changes detected
  • マイグレート実施
(py36) [tenn25@ip-10-0-0-51 blog]$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
  • 一旦 Python 内蔵サーバで Web アプリケーションを起動

アプリケーションサーバをいれなくても、
Python 自体にその機能がある。一旦それで起動。

(py36) [tenn25@ip-10-0-0-36 blog]$ python manage.py runserver 0.0.0.0:8000
Performing system checks...

System check identified no issues (0 silenced).
October 21, 2018 - 02:33:54
Django version 2.1.2, using settings 'blog.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
  • 動作確認
    [IP 直打ちの:8000 番ポート] へアクセス →OK!!!

4. Django の設定

  • Django アプリケーション上の Admin ユーザを作成

Django には元から Web ページの管理者用ページが用意されている。
その管理者ユーザを作成する。

Django は manage.py からいろんな作業を行う。
manage.py がある階層で以下のコマンドを実行。

(py36) [tenn25@ip-10-0-0-51 blog]$ python manage.py createsuperuser
ユーザー名 (leave blank to use '*****'): **********
メールアドレス: **********@*****.***
Password:
Password (again):
Superuser created successfully.
  • Admin 画面に入れることを確認

5. Gunicorn での起動

次はアプリケーションサーバの gunicorn つかう。
フォルダ内の wsgi.py を使うという指定をする

(py36) [tenn25@ip-10-0-0-51 blog]$ which gunicorn
opt/blog/py36/bin/gunicorn

(py36) [tenn25@ip-10-0-0-51 blog]$ gunicorn --version
gunicorn (version 19.9.0)


(py36) [tenn25@ip-10-0-0-51 blog]$ pwd
/opt/blog/blog/blog

(py36) [tenn25@ip-10-0-0-51 blog]$ gunicorn --bind 0.0.0.0:8000 blog.wsgi
[2018-10-21 21:15:36 +0900] [1484] [INFO] Starting gunicorn 19.9.0
[2018-10-21 21:15:36 +0900] [1484] [INFO] Listening at: http://0.0.0.0:8000 (1484)
[2018-10-21 21:15:36 +0900] [1484] [INFO] Using worker: sync
[2018-10-21 21:15:36 +0900] [1487] [INFO] Booting worker with pid: 1487
  • ブラウザから動作確認
    Admin ページの CSS が適用されない。
    アプリケーションサーバが変わって、 ルートディレクトリが変わったからっぽい。
    Django の static ファイルの扱いがなんか難しい。あとで調整しよう。
  • gunicorn の自動起動設定

.service ファイルを作ってサービスとして登録しよう

(py36) [tenn25@ip-10-0-0-51 ~]$ sudo vi /etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=tenn25
Group=tenn25
WorkingDirectory=/opt/blog/blog
ExecStart=/opt/blog/py36/bin/gunicorn --access-logfile - --workers 3 --bind 0.0.0.0:8000 blog.wsgi:application

[Install]
WantedBy=multi-user.target

いったん上記の設定をして確認しよう。
注意点として、blog.wsgi というのは[blog/wsgi.py]を意味している
wsgi.py へのパスが通るように /opt/blog/blog/blog/wsgi.py となるように WorkingDirectory にも注意すること。

  • サービスの再読み込みと自動起動設定
[tenn25@ip-10-0-0-36 system]$ sudo systemctl daemon-reload
[tenn25@ip-10-0-0-36 system]$ sudo systemctl restart gunicorn
[tenn25@ip-10-0-0-36 system]$ sudo systemctl enable gunicorn
[tenn25@ip-10-0-0-36 system]$ sudo systemctl status gunicorn
● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: disabled)
   Active: active (running) since 日 2018-10-21 12:36:40 JST; 9s ago

問題なければ、bind をソケットファイルに変更しよう。
Web サーバとアプリケーションサーバは同じサーバを想定してるので、http ではなく、
Unix ドメインソケットを使ったサーバ内部のソケット通信にすることで、通信速度が早くなる。
(gunicorn はこれに対応している。)

そのために、上記の bind 設定を以下のように変更する。

# ExecStart=/opt/blog/py36/bin/gunicorn --access-logfile - --workers 3 --bind 0.0.0.0:8000 blog.wsgi:application
ExecStart=/opt/blog/py36/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/tenn25/blog/blog.sock blog.wsgi:application

こうすることで、8000 番ポートは使わずに 80 番 →Nginx→Unix ドメインソケット →gunicorn という流れになる。   サービスファイルを変更したら、改めて systemctl で再読み込み/再起動をしよう。


6. Nginx 経由でアクセスするための設定

  • 元の設定ファイルは要らないので反映されないようにする
cd /etc/nginx/conf.d/
sudo mv default.conf default.conf.org
sudo cp default.conf.org blog.conf
  • blog.conf を編集
upstream app_server {
    server unix:/opt/blog/blog/blog.sock;
}

server {
    listen 80;
    server_name www.tenn25.com;

    location = /favicon.ico {access_log off; log_not_found off;}

    location / {
                include proxy_params;
                proxy_pass http://app_server;
                # proxy_pass http://localhost:8000; ←8000番でgunicornが受ける場合はこう
    }
}
  • ソケット通信をするには proxyparams モジュールのインポートが必要
    proxy
    params が無い場合は作成
$vi /etc/nginx/proxy_params

proxy_set_header Host $http_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-Proto $scheme;

https://webcurtaincall.com/articles/1

7. 詰まったところ

  • nginx と gunicorn は同じ実行ユーザで!!!
  • SELinux によるエラー
    安易な SELinux 無効化は反対なんだけど、エラー出るしうまく回避できなかったので、慈悲はなかった・・・・
(py36) [tenn25@ip-10-0-0-51 nginx]$ sudo cat /var/log/audit/audit.log | grep nginx | grep denied
type=AVC msg=audit(1540124576.312:226): avc:  denied  { name_connect } for  pid=1618 comm="nginx" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
type=AVC msg=audit(1540124576.312:227): avc:  denied  { name_connect } for  pid=1618 comm="nginx" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
type=AVC msg=audit(1540124719.818:241): avc:  denied  { name_connect } for  pid=1618 comm="nginx" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
type=AVC msg=audit(1540124719.818:242): avc:  denied  { name_connect } for  pid=1618 comm="nginx" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
  • 階層間違えやすいから注意

どこのパスを示してるのかは、常に注意すること。


ここまでで、各サービス再起動してあげればブラウザの 80 番ポートからアクセスできるはず。
お疲れ様でした。

Tags:  Others