Edited at

CentOSにApacheをインストールしてFlaskアプリケーションをデプロイする

この記事では、Flaskで作成したアプリケーションをさくらのVPSを利用して公開する手順を書きます。


概要

さくらのVPS上に CentOS7 をインストールして、Apacheで Webサーバーを立て、gunicorn を AP サーバーとして、Flask アプリケーションを動かします。また、Heroku と同様に git を利用したデプロイを可能にします。

自分用のメモですが、手順通りに進めれば、ある程度、再現できるようになっていると思います。再現できない部分があれば、コメントにて教えて頂けると助かります。

なお、Webサーバーを運用するうえで必要なセキュリティ設定は、この記事に書かれている内容では不十分かもしれません。セキュリティには十分に配慮したうえで、必要な設定は適宜ご自身で補って頂くようにお願いいたします。


対象とする読者


  • さくらのVPSを使ってWebサービスを公開したい人

  • Herokuしか使ったことがなく、自分でサーバーを立ててデプロイまでやってみたい人

  • Webサービスを公開したい個人開発者


利用環境


  • さくらのVPS

  • CentOS7

  • Python 3.7.4

  • Flask 1.1.1

  • Apache 2.4.6

  • gunicorn 19.9.0


1. さくらのVPSを契約

まず さくらのVPSの公式サイトから、VPSを契約します。

今回は、1Gプランの東京第2ゾーン、ストレージSSD30GBを選択しました。

2週間は無料でお試しできるようです。


2. CentOS7をインストール

さくらのVPSを契約すると、さくらのVPSコントロールパネルにログインできるようになります。

コントロールパネルで先ほど契約したサーバーを選択します。

サーバーを起動する前に、OSの設定で CentOS7 をインストールするように変更します。

初期状態では、CentOS6 がインストールされるので注意が必要です(CentOS 6 と 7 で、使用可能なコマンドに差異があります。そのため、CenOS6では、この記事の通りに進められない可能性があります。CentOS7をインストールしましょう)。

コントロールパネルの右側にある「各種設定」ボタンをクリックして、「OSインストール」でインストール画面に遷移します。

標準OSインストールで「CentOS7 x86_64」を選択し、管理ユーザーのパスワードを設定します。

管理ユーザーのパスワードは必ず強固なものを設定しましょう。簡単なパスワードを設定していると、簡単に不正ログインされます。

また、ここで設定したパスワードは、インストール後、CentOSにログインする際に使用します。忘れないようにしましょう。

スタートアップスクリプトは設定せず、インストールを開始します。

サーバーの電源状態が「稼働中」になれば、インストールは完了です。


3. CentOS の初期設定

次に、CentOS の初期設定をします。


3.1. サーバーに ssh 接続

まず、vps上にインストールした CentOS に、sshで接続します。適当なターミナルで以下のコマンドを実行します。

ssh root@[契約したサーバーのIPアドレス]

IPアドレスは、コントロールパネルでサーバーの画面を開いた後、サーバー名の下に書いてあります。

まだ、何も設定もしていないため、rootユーザーでsshで接続します。

パスワードを求められるので、先ほど設定したパスワードでログインします。


3.2. 一般ユーザーを作成

ログインしたら、一般ユーザーを作成します。rootユーザーは権限が大きく、rootユーザーですぐにログインできる状態は、セキュリティ上、非常に危険なためです。

限られた権限だけを持つ一般ユーザーを作成して、必要最低限の権限を与え、rootユーザーでのログインは制限します。


3.2.1 一般ユーザーを作成

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

adduser [ユーザー名]

ユーザー名を qiita にするなら、以下のようにします。

adduser qiita 

これで、qiitaという名前のユーザーが作成されました。

※ここでは簡単に qiita という名前にしていますが、実際には推測されにくい名称を付けるべきです。特に、admin や root-admin などといった名称は、辞書攻撃に弱く、セキュリティ上のリスクになります。


3.2.2 一般ユーザーにパスワードを設定

作成したユーザーにパスワードを設定します。以下のコマンドを実行します。

passwd [ユーザー名]

ユーザー名が qiita なら

passwd qiita 

で設定できます。

Changing password for user qiita.

New password:
Retype new password:

という表示になるので、パスワードを設定します。ここでも、推測されにくい複雑なパスワードを設定しましょう。「***」のような文字は出てきませんが、入力されています。

パスワードが設定できると

passwd: all authentication tokens updated successfully.

と表示されます。


3.3. 一般ユーザーでログイン

パスワードを設定したら、一般ユーザーでログインし直します。exitコマンドでいったん接続を切ります。

exit

その後、再度、一般ユーザーで ssh 接続します。

ssh [ユーザー名]@[サーバーのIPアドレス]

パスワードを求められるので、先ほど設定したパスワードを入力します。正しくユーザーが追加できていれば、ssh 接続できます。


3.4. rootログインを禁止する

一般ユーザーでログインできるようになりましたが、

このままでは、一般ユーザーでも root ユーザーでもログインできてしまいます。

rootユーザーによるログインを禁止しましょう。

設定のため、rootユーザーでログインします。

rootユーザーでログインしたら、以下のコマンドを実行します。

cd /etc/ssh

vim sshd_config

Linux系の OS では、etcディレクトリに、設定ファイルが配置されます。

その下の ssh のディレクトリに ssh の設定ファイルがあります。

cd (= change directory) コマンドで /etc/ssh に移動し、sshd_config (= ssh daemon configuration) ファイル を vim で開きます。

vimsshd_config ファイルを編集します。

40行目前後に以下のようなコメントアウトされた行があります。

#PermitRootLogin yes

PermitRootLogin (rootログインを許可する)という設定を書いている行です。この先頭の # を削除し、「yes」を「no」に変更します。

PermitRootLogin no

変更したら :wq で保存して終了します。

このままだと変更が反映されていないので、以下のコマンドでsshd を再起動します。

systemctl restart sshd.service

注:CentOS6の場合は systemctl ではなく service コマンドを使います。


3.4. 公開鍵認証に変更

rootユーザーによるログインを禁止したら、公開鍵認証によるログインに変更します。そして、パスワードによるログインは禁止します。


3.4.1. 公開鍵と秘密鍵を作成

まずは、公開鍵と秘密鍵のペアを作成します。

ローカルのターミナルで、以下のコマンドを実行します。

ssh-keygen

デフォルトでは .ssh 以下のディレクトリに id_rsa (秘密鍵) , id_rsa.pub (公開鍵) が作られます。必要があれば、パスやファイル名を変更しましょう。


3.4.2. 公開鍵をリモートにコピー

公開鍵/秘密鍵ファイルを作成したら、公開鍵を VPS 上の CentOS に scp コマンドでコピーします。 scp コマンドは、ssh 通信を利用して、ファイルをコピーするコマンドです。

scp id_rsa.pub [ユーザー名]@[サーバーのIP]:[保存先のパス]

リモートに ssh でログインして、コピーした公開鍵を authorized_keys という名称に変更し ~/.ssh ディレクトリにコピーします。

.ssh ディレクトリがなければ、以下のように作成します。

mkdir /home/qiita/.ssh

cat /home/qiita/id_rsa.pub >> /home/qiita/.ssh/authorized_keys

その後 id_rsa.pub は削除します。

rm /home/qiita/id_rsa.pub


3.4.3. パスワード認証を禁止

公開鍵認証を使えるようになったので、パスワードによる認証は禁止します。

リモートの CentOS に一般ユーザーでログインした後、su - コマンドでスーパーユーザーに切り替え、下記のコマンドで設定ファイルを開きます。

vim /etc/ssh/sshd_config

60行目くらいにある PasswordAuthentication を no に変更します。

PasswordAuthentication yes  => PasswordAuthentication no

:wq で保存して終了し、以下のコマンドで再起動して、変更を反映させます。

systemctl  restart  sshd.service


3.4.4. ssh 接続を楽にする

さて、ここまで ssh 接続で ssh [ユーザー名]@[ip] のようなコマンドを打ってきましたが、

毎回このコマンドを打つのは面倒なので、手続きを簡単にします。ローカルで、以下のコマンドを実行し、~/.ssh 以下に、設定ファイル config を作ります。

touch ~/.ssh/config

vimでこれを開き、編集します。

vim ~/.ssh/config

そして、以下のように設定を記述します。

Host [接続時に使う名前]

HostName [hostのIPアドレス]
Port [Port番号]
User [ユーザー名]
IdentityFile [秘密鍵ファイルのパス]

例として、以下のように書きます。

注:本来ならポートは 22 以外のポートに変更すべきです。その方法は後で追記するかも。

Host vpsapp

HostName xxx.xx.xxx.xx
Port 22
User qiita
IdentityFile ~/.ssh/id_rsa

このように設定を書くと、以下のコマンドだけで、ssh 接続できるようになります。

ssh vpsapp

ホスト名やユーザー名を毎度入力しなくて済みます。


4. Apacheをインストール

ssh 接続の設定を終えたら、続いて Apache HTTP Server をインストールします。Apacheは、サーバーソフトウェアであり、これをインストールすることによって、ただの Linuxマシンが Webサーバーとしての役割を果たすようになります。

サーバーソフトウェアとしては、nginx がナウいですが、今回は、Apacheをインストールします。

以下のコマンドで Apache HTTP Server をインストールします。

yum install httpd

yum は CentOS のパッケージ管理システムです。 yum install [パッケージ名] で指定したパッケージをインストールできます。

コマンド実行後、yes/no を問われたら yes を選択します。

インストールしたら、下記コマンドで、Apache を起動します。

systemctl start httpd

コマンド実行後、以下のコマンドで起動できたか確認します。

systemctl status httpd

Active: active (running) と表示されれば、起動できています。


5. Firewallの設定

続いて Firewall の設定をします。Firewallは、ネットワーク間のアクセスを制御するソフトウェアです。下記コマンドで Firewalld が起動していることを確認します。

firewall-cmd --state

状態がnot runningなら、下記コマンドで起動します。

systemctl start firewalld.service

起動したら、必要な設定を行います。ここでは publicゾーンに対して http と https の通信を許可します。必要に応じて Firewall の設定を追加してください。

firewall-cmd --add-service=http --zone=public --permanent

firewall-cmd --add-service=https --zone=public --permanent

設定後 Firewalld を再起動します。

systemctl restart firewalld.service

ここでブラウザのアドレスバーに ip アドレスを入力してアクセスすると、Apacheのデフォルトのページが表示されると思います。

※2019年06月27日から、さくらのVPSがパケットフィルタ機能を導入しており、コントロールパネルから Web (80/443) の接続を別途許可する必要があるようです。デフォルトでは、22番ポートのみ、アクセスが許可されています。


6. ドキュメントルートの権限を設定して html を返す

Firewallが設定できたら、htmlのページを返せるようにします。その前に、権限設定をします。

具体的には、Apacheのドキュメントルート(/var/www/html)に html ファイルを配置して、ブラウザからアクセスした時に html のページが表示されるようにします。

以下のように、適当なhtmlファイルを作成します。


index.html

<html>

<head>
<title>Test</title>
</head>
<body>
Test
</body>
</html>

これを scp コマンドでリモートの /var/www/html ディレクトリにコピーします。ローカルで、以下のコマンドを実行します。

scp index.html [ユーザー名]@[ホスト]:/var/www/html

前述の方法で config ファイルに ssh 接続の設定を書いている場合は、以下のように実行することができます。

scp index.html vpsapp:/var/www/html

現時点では、権限設定ができていないため、以下のようなエラーが出て、コピーできません。

scp: /var/www/html/index.html: Permission denied

そこで、リモートにログインして権限設定をします。

一般ユーザーでログインした後、su - コマンドで root ユーザーに切り替えます。

ssh testvps

su -

以下のコマンドで /var/www 以下のファイルやディレクトリの権限設定を確認します。

ls -l /var/www 

以下のような表示になります。

drwxr-xr-x 2 root root 4096 Apr 24 22:46 cgi-bin

drwxr-xr-x 2 root root 4096 Apr 24 22:46 html

drwxr-xr-x の部分が、ファイルのパーミッションを示しています。

現時点では、所有者である root ユーザーのみ、htmlディレクトリへの書き込みが許可されていることがわかります。

以下のコマンドで、所有者/所有グループを変更します。

chown apache:qiita /var/www/html

chown は change owner の意で、所有者/所有グループを変更するコマンドです。

次に、以下のコマンドで、権限を変更します。

chmod 775 /var/www/html

chmod は change mode の意で、パーミッションを変更するコマンドです。

もう一度、ls コマンドでパーミッションが変更されたかを確認します。

ls -l /var/www 

以下のように変更されていればOKです。

drwxrwxr-x 2 apache qiita 4096 Apr 24 22:46 html

ここまでできたら、ローカルに戻って、もう一度、先ほどの scp コマンドを実行します。

$ scp index.html vpsapp:/var/www/html

index.html 100% 108 8.3KB/s 00:00

これで、apacheのドキュメントルートに index.html が追加されました。試しに、ブラウザからアクセスしてみると、以下のように html で作った Test ページが表示されます。


7. git でデプロイできる環境をつくる

次に、Pythonファイルを含む、Flaskアプリケーションをリモートにデプロイできるようにします。

ここまで、リモートへのファイル転送には scp コマンドを使っていました。引き続き scp でファイル転送をしてもよいですが、git のコミット時に、自動的にデプロイされると便利です。そこで、今回は git でデプロイできる環境を作ります。

GitHub や Heroku に push するのと同様の手順で、サービスをデプロイできるようになります。


7.1. リモートリポジトリを作る

まず、リモートのサーバーに ssh 接続して、以下のように git のリモートリポジトリを作成します。

mkdir /home/qiita/gitremote

cd /home/qiita/gitremote
git init --bare vpsapp.git

git init コマンドに --bare オプションをつけると、bare リポジトリが作成されます。

bareリポジトリは、作業ディレクトリを含まず、commit や push の履歴を管理するリポジトリです。


7.2. ローカルで remote リポジトリを追加

次に、ローカルで remote リポジトリを追加します。

$ git init

$ git remote add origin vpsapp:/home/qiita/gitremote/vpsapp.git

GitHub 等のリモートリポジトリが既に追加されている場合、

fatal: remote origin already exists.

のように表示され、失敗します。

そこで、ローカルの .git/config を編集して、2つのリモートリポジトリに push できるように設定を追加します。

vim .git/config

[remote "origin"] のところを、以下のように編集します。

[remote "origin"]

url = https://github.com/hogeuser/hoge.git
url = testvps:/home/[ユーザー名]/gitremote/[プロジェクト名].git <-- 追加
fetch = +refs/heads/*:refs/remotes/origin/*

これで push 時に両方のリモートリポジトリに push されます。

ローカルで適当にファイルを変更して commit してから push してみます。

git push origin master

追加したリモートリポジトリに push されたことを ssh 接続して確認します。

cd /home/qiita/gitremote/vpsap.git

git log

無事に push できていれば、これまでの commit ログが表示されるはずです。


7.3. ドキュメントルートに clone する

ここで push したリポジトリは、bareリポジトリなので、作業ディレクトリを持っていません。

代わりに commit されたとか、pushされた、という情報を持っています。

git push 時に変更がデプロイされるように、ドキュメントルートで、このディレクトリ clone できるようにします。

su - でルートユーザーに切り替えて操作します。

$ cd /var/www/html

$ git clone /home/qiita/gitremote/vpsapp.git

以下のような表示が出て clone が完了します。

Cloning into 'vpsapp'...

done.

そのまま、lsコマンドを打つと、ローカルにあったプロジェクトがコピーされていることが分かります。

$ ls

vpsapp


7.4. push 時に自動で clone する

push する度に手作業で clone するのは手間です。自動化しましょう。

自動化のために git hook を利用します。

cd /home/qiita/gitremote/vpsapp.git/hooks

vim post-receive

以下を記述します。

cd /var/www/html/vpsapp

git --git-dir=.git pull

実行権限を与えます。

chmod a+x hooks/post-receive

ローカルで適当に commit して push すると、ドキュメントルートに自動的に clone され、変更が反映されます。


8. Flaskアプリケーションを公開

さて、いよいよ、Pythonをインストールして、Flaskアプリケーションを公開できるようにします。流れは以下のようになります。


  1. pipenv で Python3 の環境構築

  2. Flask と Apache を接続

なお、今回公開する Flask アプリケーションは、以下のような構成で /var/www/html 以下に用意されているものとします。

vpsapp

└── app
├── templates
├── static
└── app.py
└── run.py


8.1. pipenv で Python3 の環境構築

まずは、Python3 を使えるようにします。そのままインストールしてもいいですが、今回は Pipenv を使います。


8.1.1. pipのインストール

まず、Python のパッケージ管理システム pip をインストールします。

get-pip.py を curl コマンドで取得して、実行します。

curl https://bootstrap.pypa.io/get-pip.py | sudo python

これで pip がインストールされます。

$ pip --version

pip 19.1.1 from /usr/lib/python2.7/site-packages/pip (python 2.7)


8.1.2. pyenvのインストール

pyenv のリポジトリをクローンします。

git clone https://github.com/pyenv/pyenv.git ~/.pyenv

pyenv に path を通します。

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile

echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile
exec "$SHELL" -l

次のコマンドで、pyenvがインストールされたことを確認します。

$ pyenv --version

pyenv 1.2.13

また、開発に必要なもろもろのパッケージをインストールします。

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

参考:pyenv を使うために必要なパッケージはここに書いてあります。


8.1.3. pipenvのインストール

最後に pipenv をインストールします。

sudo pip install pipenv

インストールできたか確認します。

$ pipenv --version

pipenv, version 2018.11.26


8.1.4. ドキュメントルートに Python3 環境を構築

以下のコマンドを実行し、Apache のドキュメントルートで、Python3.7.4 を指定します。

インストールされていなければインストールします。

しばらく時間がかかります。

$ cd /var/www/html

$ pipenv --python 3.7.4

Would you like us to install CPython 3.7.4 with pyenv? [Y/n]: Y

以下のようなメッセージが完了です。

OK Successfully created virtual environment! 

pipenv の仮想環境で、バージョンを確認します。

$ pipenv shell

$ python --version
Python 3.7.4

Python3.7.4 の環境が作れました。


8.2. Flask と Apache を接続

最後に Apache で受けたリクエストを Flask に渡して、動くようにします。


8.2.1. WSGIとは

Pythonでは、WSGIという仕様に従うことで、サーバーを選ばずに、Python の Webアプリケーションが動くように決められています。WSGIについてはこちら を参照してください。


8.2.2. uwsgi, gunicorn, mod_wsgi

WSGI 仕様に則った AP サーバーとして gunicorn や uwsgi があります。また mod_wsgiというモジュールを使うことによって、Apache で Flask を動かすこともできます。

gunicorn を APサーバーとして、Apache で受けたリクエストを gunicorn にプロキシするのが簡単なので、今回は gunicorn を使います。


8.2.3. gunicorn をインストール

/var/www/html/vpsapp 以下で

$ pipenv install gunicorn

$ pipenv shell
$ gunicorn run:app

で 8000番ポートで Flaskアプリケーションが動きます。

run:app は [Flaskオブジェクトのある.pyファイル]:[Flaskオブジェクトの入っている変数]という指定をしています。


8.2.4. Apache で受けたリクエストを gunicorn にプロキシ

Apache で 80番、443番で受けた http/https リクエストを gunicorn にプロキシします。

vim /etc/httpd/conf/httpd.conf

最終行に、以下のように追記します。

ProxyPass / http://localhost:8000/

これで、http リクエストが8000番ポートにプロキシされ、作成した Flask アプリケーションにブラウザからアクセスできるようになります。


8.2.5. サーバー起動時に gunicorn を起動する

※編集中

$ cd /etc/systemd/system/

$ sudo vim app.service

以下のように書きます。

[Unit]

Description=gunicorn daemon
After=network.target

[Service]
User=apache
Group=apache
WorkingDirectory=/var/www/html/vpsapp
ExecStart=/bin/pipenv gunicorn run:app

[Install]
WantedBy=multi-user.target

で、enable コマンドでサーバー起動時に gunicorn が起動するようにします。

$ systemctl enable app

$ systemctl start app

ついでに、サーバー起動時に apache が起動するようにしておきます。

$ systemctl enable httpd.service

質問・指摘はコメント欄か、twitterアカウント @sti320aにお願いいたします。