その5 からの続きです。作った Flask アプリを EC2 にデプロイします。
EC2 側の環境設定
Heroku のような PaaS (Platform as a Service) では、環境が既に設定されており、作ったコードを Git などでそのままアップするだけで稼働します。一方で EC2 は汎用仮想環境であり、初期状態では何も設定されていない、いわば無垢な状態です。まずはこれに Python やら何やらをインストールしていく必要があります。
その3 でSSH通信のためのキーペアを作成し、秘密鍵を keypair.pem
としてダウンロードしていたので、それを使って通信を行います。このファイルはホームディレクトリ ~/
にあるものとして進めます。他の場所に置いている場合、以下のコマンドは適宜読み替えて下さい。
まず AWS の EC2 Dashboard へ行き、インスタンスを選択します。ついでに Name をクリックして名前でもつけてあげましょう、ここでは testapp
としておきました。Connect を押すと、こんな画面が出てくるかと思います。これが繋ぎ方の説明です。
コンソールを開き、まずは 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 で固定したものと同じです。
この後は、まずパッケージ管理ツール 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バーに入力します。
アクセスできました。これで(超)簡易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の公式ドキュメントにもきちんと書いてあります。これは後々修正していきます。
現段階のこの貧弱なアプリでは攻撃を受けたりすることは99.9%ないと思われますが、心配であれば使わない時にインスタンスを一旦停止させておくのも良いかと思います。
コンソールの Stop instance から停止でき、Start instance から再開できます。再開した後は上と同様の手順でまたアプリを起動すれば良いです。
その7 に続きます。