この記事では、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
で開きます。
vim
で sshd_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ファイルを作成します。
<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アプリケーションを公開できるようにします。流れは以下のようになります。
- pipenv で Python3 の環境構築
- 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にお願いいたします。