[無料] AWS EC2 + RDS + Flask でWebアプリを作り HTTPS 化する [その6: EC2へのデプロイ]

[その5](https://qiita.com/nozomiyamada/items/8d75ff32dad5b0976738) からの続きです。作った Flask アプリを EC2 にデプロイします。

## EC2 側の環境設定

Heroku のような PaaS (Platform as a Service) では、環境が既に設定されており、作ったコードを Git などでそのままアップするだけで稼働します。一方で EC2 は汎用仮想環境であり、初期状態では何も設定されていない、いわば無垢な状態です。まずはこれに Python やら何やらをインストールしていく必要があります。

[その3](https://qiita.com/nozomiyamada/items/0be5844568eeceeca51d) でSSH通信のためのキーペアを作成し、秘密鍵を `keypair.pem` としてダウンロードしていたので、それを使って通信を行います。このファイルはホームディレクトリ `~/` にあるものとして進めます。他の場所に置いている場合、以下のコマンドは適宜読み替えて下さい。

まず AWS の **EC2 Dashboard** へ行き、インスタンスを選択します。ついでに Name をクリックして名前でもつけてあげましょう、ここでは `testapp` としておきました。**Connect** を押すと、こんな画面が出てくるかと思います。これが繋ぎ方の説明です。
![Screen Shot 2563-12-13 at 17.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/ab3d31b6-48c3-451e-7751-a3ac1053fe03.png)
コンソールを開き、まずは 3. にあるように、秘密鍵の読み取り権限を自分のみに変更します。 これで万一他者に自分のPCに不正アクセスされても大丈夫です(その状況は全然大丈夫じゃないけど)。

~~~
$ chmod 400 ~/keypair.pem
~~~

**Example** の例にしたがって、以下のように自分の EC2 インスタンスのアドレスを入力します。`ssh -i <秘密鍵の場所> <通信先>` となっています。後半はどのリージョンを使っているかによって変わります。この例ではシンガポールリージョンなので ap-southeast-1 になっています。

~~~
$ ssh -i ~/keypair.pem ec2-user@ec2-18-XXX-XXX-XX.ap-southeast-1.compute.amazonaws.com
~~~

最初は認証を確認するメッセージが出てくるかもしれません、`yes` と入力して続行します。接続に成功すると以下のようになるはずです。

~~~
https://aws.amazon.com/amazon-linux-2/
7 package(s) needed for security, out of 19 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-172-XX-XX-XXX ~]$
~~~

ここで表示される `ec2-user@ip-` のあとの数字はプライベートIPアドレスであり、外部からアクセスする時には使いません。パブリックIPアドレスは接続時の `ec2-user@ec2-` の後の数字で、[その4](https://qiita.com/nozomiyamada/items/b7a0e41f8117a34ba3fa) で固定したものと同じです。

この後は、まずパッケージ管理ツール `yum` を更新します。Ubuntu での `apt update` と同じです。

~~~
[ec2-user@ip-172-XX-XX-XXX ~]$ sudo yum update
~~~

また、デフォルトで入っているのは Python2 のみなので、Python3 のインストールをしておきます。2020年12月現在では Python3.7.9 がインストールされましたが、もし自分の使っているバージョンが違うなら指定してインストールした方がいいかもしれません。まあPython自体のバージョンは3.6以降ならあまり差がないので、余程特殊なことをしない限り特に問題は起きないかと思います。自分の使っているPythonのバージョンは `python3 -V` で確認できます。

~~~
[ec2-user@ip-172-XX-XX-XXX ~]$ sudo yum install python3
~~~

ちなみにローカルで構築した仮想環境をPython自体も含めて丸ごとそのままコピーすれば起動することもできるかもしれませんが、今後別のアプリや仮想環境を作ったりすることもありますし、何よりローカルから一々全部コピーするのは相当手間がかかります。

EC2 内にローカル同様に仮想環境を構築し、ライブラリをインストールしていきます。他のアプリは同じサーバー内に絶対に作らないのであれば、システムにそのままインストールしてしまってもいいですが、とりあえずここでは仮想環境を用意しておきます。フォルダを作成し、そこに作ります。

~~~
[ec2-user@ip-172-XX-XX-XXX ~]$ mkdir testapp
[ec2-user@ip-172-XX-XX-XXX ~]$ cd testapp
[ec2-user@ip-172-XX-XX-XXX testapp]$ python3 -m venv .venv
[ec2-user@ip-172-XX-XX-XXX testapp]$ ls -a
.  ..  .venv 
~~~

`ls -a` コマンドで、仮想環境ができていることを確認できます。`-a` は隠しファイルも表示するオプションです。

仮想環境を有効化し、Flask をインストールします。

~~~
[ec2-user@ip-172-XX-XX-XXX testapp]$ source .venv/bin/activate
(.venv)[ec2-user@ip-172-XX-XX-XXX testapp]$ pip install flask
~~~

今は Flask 以外の外部ライブラリを全く使っていないのでこれでいいですが、今後他のライブラリもたくさん使うようになったら、`requirements.txt` というものを作成し、それを用いて同一のバージョンのライブラリを一気にインストールする方法がよく使われます。

環境設定はこれで終わりです、`exit` と入力して一旦通信を終了します。

## ローカルからのファイルのコピーと起動

先ほどの通信は直接コマンドを入力しましたが、今度は `scp` コマンドで EC2側へファイル転送を行います。フォルダごとコピーする場合は `-r` オプションをつけて、`scp -r -i <秘密鍵の場所> <コピーするフォルダの場所> <通信先>:<コピー先>` と入力します。今回の場合、ホームディレクトリ内に `/testapp` を作成していたので以下のようになっています。必要なファイルは `app.py` と `/templates` だけです。`static` はまだ中身がないのでコピーしなくても良いです。

~~~
$ scp -i ~/keypair.pem ~/testapp/app.py ec2-user@ec2-18-XXX-XXX-XX.ap-southeast-1.compute.amazonaws.com:~/testapp
app.py                                        100%  922    20.5KB/s   00:00

$ scp -r -i ~/keypair.pem ~/testapp/templates ec2-user@ec2-18-XXX-XXX-XX.ap-southeast-1.compute.amazonaws.com:~/testapp
form.html                                     100%  371    10.7KB/s   00:00
layout.html                                   100%  296     3.2KB/s   00:00
top.html                                      100%   96     2.3KB/s   00:00
~~~

送信が成功すればこのようになります。Git をインストールして差分の管理をしたりもできなくはないのですが、それはまた別の機会にし、ここでは原始的な方法で必要ファイルのみアップロードしていきます。再び SSH で EC2 にアクセスし、ファイルがあるか確認します。

~~~
$ ssh -i ~/keypair.pem ec2-user@ec2-18-XXX-XXX-XX.ap-southeast-1.compute.amazonaws.com
[ec2-user@ip-172-XX-XX-XXX ~]$ cd testapp
[ec2-user@ip-172-XX-XX-XXX testapp]$ ls
app.py  templates
~~~

ここでアプリを起動します。`sudo` による root 権限が必要です。どのディレクトリにいても起動できるように、パスを絶対指定しています。

~~~
[ec2-user@ip-172-XX-XX-XXX testapp]$ sudo ~/testapp/.venv/bin/python ~/testapp/app.py
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 177-237-304
~~~

こうなれば起動成功です。このまま、パブリックIP (接続先 `ec2-user@ec2-` の後ろの数字 `XX.XXX.XXX.XX`) をブラウザのURLバーに入力します。
![Screen Shot 2563-12-13 at 19.59.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/0670ce65-a9b0-6d25-6eda-42a093ebbd27.png)
アクセスできました。これで(超)簡易Webアプリの完成です。

## バックグラウンド実行

アプリの起動自体は成功しているのですが、`command + C` や `ctrl + C` で一旦コマンドを中断したり、SSH通信からログアウトしてしまうとプロセスが止まってしまい、ページにアクセスできなくなります。これを防ぐためにバックグラウンド実行し、プロセスを終了させないようにする必要があります。ここで `nohup` というコマンドを使います。

一旦コンソールを閉じて強制終了し、再度SSH通信します。ここで以下のように入力します。

~~~
$ ssh -i ~/keypair.pem ec2-user@ec2-18-XXX-XXX-XX.ap-southeast-1.compute.amazonaws.com
[ec2-user@ip-172-XX-XX-XXX ~]$ sudo nohup ~/testapp/.venv/bin/python ~/testapp/app.py &
[1] 9604
[ec2-user@ip-172-XX-XX-XXX ~]$ nohup: ignoring input and appending output to 'nohup.out'

[ec2-user@ip-172-XX-XX-XXX ~]$
~~~

このプロセスのIDが表示された後、コマンド待機状態に戻ります。また、`exit` で通信を終了しても、ページにアクセスできる状態になっています。これでとりあえずはWebアプリとして機能するようになります。

もしこのアプリを終了させたい時には、`ps aux` コマンドを使ってプロセスIDを探し、それを `kill` する必要があります。

~~~
[ec2-user@ip-172-XX-XX-XXX ~]$ ps aux
...
root      9537  0.0  0.0      0     0 ?        I    13:15   0:00 [kworker/0:2]
root      9556  0.0  0.0      0     0 ?        I    13:21   0:00 [kworker/0:1]
root      9604  0.0  0.7 241772  7296 ?        S    13:22   0:00 sudo nohup /home/ec2-user/testapp/.venv/bin/python /home/ec2-user/testapp/app.py
root      9605  0.0  2.6 239400 26576 ?        S    13:22   0:00 /home/ec2-user/testapp/.venv/bin/python /home/ec2-user/testapp/app.py
root      9606  0.3  2.7 467016 27204 ?        Sl   13:22   0:00 /home/ec2-user/testapp/.venv/bin/python /home/ec2-user/testapp/app.py
root      9657  0.0  0.8 152628  8844 ?        Ss   13:25   0:00 sshd: ec2-user [priv]
ec2-user  9675  0.0  0.4 152628  4584 ?        S    13:25   0:00 sshd: ec2-user@pts/5
ec2-user  9676  0.2  0.3 124848  3928 pts/5    Ss   13:25   0:00 -bash
ec2-user  9703  0.0  0.3 164336  3888 pts/5    R+   13:25   0:00 ps aux
~~~

このように現在実行中のプロセスが一覧表示されます。この場合、`9604` がさっき実行したプロセスIDだとわかるので、これを

~~~
[ec2-user@ip-172-XX-XX-XXX ~]$ sudo kill 9604
~~~

のように終了させることができます。

ちなみに、実行時のメッセージに

~~~
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
~~~

というものがあります。要するに「今は組み込みの開発サーバーを使っているだけだから、本番用のWSGIを使ってくれという」警告です。この辺りのことは、[Flaskの公式ドキュメントにもきちんと書いてあります](https://flask.palletsprojects.com/en/1.1.x/tutorial/deploy/)。これは後々修正していきます。

現段階のこの貧弱なアプリでは攻撃を受けたりすることは99.9%ないと思われますが、心配であれば使わない時にインスタンスを一旦停止させておくのも良いかと思います。
![Screen Shot 2563-12-13 at 20.48.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/8ca626ff-3853-f638-b12a-d696802b4fbe.png)

コンソールの **Stop instance** から停止でき、**Start instance** から再開できます。再開した後は上と同様の手順でまたアプリを起動すれば良いです。

[その7](https://qiita.com/nozomiyamada/items/6a898860d5c455868859) に続きます。