はじめに
PaaS である fly.io で Python Flaskアプリ (DB は Postgres を利用) をデプロイしてみたのでまとめています。
なお、「fly.io に Python Flask アプリをデプロイするためのローカルの作業及び開発環境を作成」にて作成した Docker コンテナ内で作業します。
fly.io サイトでアカウント登録
https://fly.io/ でアカウントを登録しておきます。
作業用の Docker コンテナを起動
「fly.io に Python Flask アプリをデプロイするためのローカルの作業及び開発環境を作成」にて作成した Docker コンテナを起動
$ docker start --interactive fly-work
woker ユーザーに su して作業ディレクトリに移動
# su - worker
$ cd ~/work/
この状態で以下の 2つのファイルが存在します。
$ tree -a ./
./
├── requirements.txt
└── testapp.py
0 directories, 2 files
flyctl をインストール
fly.io のコマンドラインツール flyctl をインストール
$ curl -L https://fly.io/install.sh | sh
環境変数を .bashrc に記載
$ echo 'export FLYCTL_INSTALL="/home/worker/.fly"' >> ~/.bashrc
$ echo 'export PATH="$FLYCTL_INSTALL/bin:$PATH"' >> ~/.bashrc
環境変数を反映
$ source ~/.bashrc
fly.io にログイン
$ flyctl auth login
-
GUI 環境が無いので以下のメッセージが出ます。
failed opening browser. Copy the url (https://fly.io/app/auth/cli/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) into a browser and continue Opening https://fly.io/app/auth/cli/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
ブラウザでメッセージ中の URL https://fly.io/app/auth/cli/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx を開き、未ログインの場合はログインし、「Continue as [登録したメールアドレス]」をクリック
作業環境で以下のメッセージとなり、flyctl でのログイン完了
successfully logged in as xxxxxxxx@xxxx.xxx
Python Flask アプリをローンチ
flyctl コマンドでローンチを実施します。しかし、ローカルに Docker イメージを作成する環境が無いためか、この段階ではデプロイまでは実施されません。
Fly アプリをローンチ
$ flyctl launch
-
アプリ名を聞いてくるので入力
? App Name (leave blank to use an auto-generated name): myflyapp
-
リージョンを聞いてくるので Tokyo を選択してみました
? Select region: [Use arrows to move, type to filter] ... 省略 ... > nrt (Tokyo, Japan)
-
Postgres DB をセットアップするか聞いてきますが、別で作成しているので、ここではセットアップしません
? Would you like to set up a Postgresql database now? No
-
最後のこのメッセージが表示され、作成された Procfile を実際にあわせて編集する必要があるとのことです
We have generated a simple Procfile for you. Modify it to fit your needs and run "fly deploy" to deploy your application.
二つのアプリが設定されました
$ flyctl apps list
NAME OWNER STATUS PLATFORM LATEST DEPLOY
fly-builder-XXXX personal suspended machines
myflyapp personal pending
-
myflyapp
flyctl launch
で設定したアプリ
-
fly-builder-XXXX
- デプロイ時に Docker Image を作成するためのリモートビルダーアプリ
- ローカルで Docker イメージを作成して Fly サイトへ push するのがデフォルトの動作のようですが、Docker デーモンがローカルに無い場合はローカルで Docker イメージを作成できないので Fly サイトにリモートビルダーアプリが作成され、これが Docker イメージを作成するとのことです。
また、Procfile と fly.toml が作成されました。
$ tree -a
.
├── Procfile
├── fly.toml
├── requirements.txt
└── testapp.py
0 directories, 4 files
fly.toml は fly アプリの設定ファイルとのことです。
Procfile の中身を確認
$ cat Procfile
# Modify this Procfile to fit your needs
web: gunicorn server:app
Procfile を以下のように書き換え
$ cat Procfile
# Modify this Procfile to fit your needs
web: gunicorn testapp:app --log-file=-
-
server:app
の部分を実際の Python スクリプトに合わせてtestapp:app
に書き換え -
testapp.py スクリプトの中の Flask オブジェクトが
app = Flask(__name__)
となっているのでtestapp:app
と指定します -
ログを取得できるように
--log-file=-
を追加
WSGI サーバーとして gunicorn を使用するので requirements.txt に追加
$ cat requirements.txt
gunicorn
Flask
Flask-SQLAlchemy
psycopg2-binary
Python Flask アプリが使用する Postgres DB 用の fly アプリを作成
Postgres DB 用の fly アプリを作成
$ flyctl postgres create
-
アプリ名を聞いてくるので入力
? Choose an app name (leave blank to generate one): mydbapp
-
リージョンを聞いてくるので Tokyo を選択してみました
? Select regions: [Use arrows to move, type to filter] ... 省略 ... > Tokyo, Japan (nrt)
-
設定を聞いてくるので以下を選択してみました。(これ以外の選択だと有料プランが必要になるかも)
? Select configuration: [Use arrows to move, type to filter] > Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk
-
DB への接続情報が表示されました。後で参照できるよう、どこかセキュアな場所に保存しておきます。(二度と確認することができないとのことです)
Postgres cluster mydbapp created Username: postgres Password: XXXXXXXXXXXXXXXXXXXXXXX Hostname: mydbapp.internal Proxy Port: 5432 Postgres Port: 5433
Postgres DB アプリ mydbapp が作成され起動していることを確認
$ flyctl apps list
NAME OWNER STATUS PLATFORM LATEST DEPLOY
fly-builder-XXXX personal suspended machines
mydbapp personal running nomad 2m49s ago
myflyapp personal pending
flyctl を使って Postgres DB に接続
$ flyctl postgres connect --app mydbapp
-
通常の psql ツールで接続されているようです。
-
ユーザー名は指定していませんが、postgres ユーザーで接続されました。パスワードは不要でした。
-
データベースを確認
postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+------------+----------+------------+------------+--------------------------- postgres | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 | template0 | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 | =c/flypgadmin + | | | | | flypgadmin=CTc/flypgadmin template1 | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 | =c/flypgadmin + | | | | | flypgadmin=CTc/flypgadmin (3 rows)
- 通常の以下のデータベースのみ作成されていました。
- postgres
- template0
- template1
- 通常の以下のデータベースのみ作成されていました。
-
接続先のデータベース名も postgres データベースでした。
postgres=# select current_database(); current_database ------------------ postgres (1 row)
-
終了
postgres=# \q
ローカルの psql ツールやローカルの Python スクリプトから Postgres DB に接続するには flyctl proxy を使うとのことです。
-
flyctl proxy 起動
$ flyctl proxy 15432:5432 --app mydbapp Proxying local port 15432 to remote [mydbapp.internal]:5432
- ローカルでリッスンする proxy ポートは 15432
- リモートの Postgres DB ポートは 5432
-
例えば、ローカルの psql ツールで接続する場合
$ psql postgres://postgres:XXXXXXXXXXXXXXXXXXXXXXX@localhost:15432/
- パスワード
XXXXXXXXXXXXXXXXXXXXXXX
は上記で控えたものを使用
- パスワード
-
psql 終了
postgres=# \q
-
flyctl proxy も Ctrl-C で終了
Python Flask アプリと Postgres DB アプリを紐づけてテーブル作成とデータ挿入
Python Flask アプリ myflyapp が Postgres DB アプリ mydbapp に接続できるよう、両者を紐づけ
$ flyctl postgres attach \
--app myflyapp \
mydbapp
Postgres cluster mydbapp is now attached to myflyapp
The following secret was added to myflyapp:
DATABASE_URL=postgres://myflyapp:XXXXXXXX@top2.nearest.of.mydbapp.internal:5432/myflyapp
- myflyapp と mydbapp が紐づけられました
- myflyapp に上記の環境変数 DATABASE_URL が設定されました
- mydbapp に以下が作成されています
- myflyapp データベース
- myflyapp ユーザー (パスワード XXXXXXXX)
ローカルの psql ツールで mydbapp に接続するための flyctl proxy 起動
$ flyctl proxy 15432:5432 --app mydbapp
Python Flask アプリ testapp.py では Flask-SQLAlchemy を使用しているため、テーブル作成やデータ挿入等は Python を使って testapp.py で作成した SQLAlchemy オブジェクトを使用します。
Python 起動
$ DATABASE_URL=postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
python3
-
後に testapp.py をモジュールとしてインポートしますが、環境変数として DATABASE_URL を参照するので設定して Python を起動してます。
-
testapp.py をモジュールとしてインポート
>>> import testapp
-
テーブル作成
>>> testapp.db.create_all()
-
作成された test_record テーブルにデータ挿入
>>> record = testapp.TestRecord() >>> record.test_col = 'Hello World' >>> testapp.db.session.add(record) >>> testapp.db.session.commit()
-
Python 終了
>>> exit()
testdb データベースが作成されていることを確認
$ psql \
postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
--command '\l'
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+------------+----------+------------+------------+---------------------------
myflyapp | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 | =c/flypgadmin +
| | | | | flypgadmin=CTc/flypgadmin
template1 | flypgadmin | UTF8 | en_US.utf8 | en_US.utf8 | =c/flypgadmin +
| | | | | flypgadmin=CTc/flypgadmin
(4 rows)
testapp.py の TestRecord オブジェクトに対応する test_record テーブルが testdb データベースに作成されていることを確認
$ psql \
postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
--command '\d'
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | test_record | table | myflyapp
(1 row)
test_record テーブルにデータが挿入されていることを確認
$ psql \
postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
--command 'select * from test_record'
test_col
-------------
Hello World
(1 row)
- flyctl proxy を Ctrl-C で終了
Python Flask アプリをデプロイ
Python Flask アプリ myflyapp をデプロイ
$ flyctl deploy
割り当てられた IP を確認
$ fly ips list
VERSION IP TYPE REGION CREATED AT
v4 XXX.XXX.XXX.XXX public global 5m19s ago
v6 XXXX:XXXX:X::XXXX public global 5m16s ago
myflyapp アプリのステータスを確認
$ flyctl status
App
Name = myflyapp
Owner = personal
Version = 0
Status = running
Hostname = myflyapp.fly.dev
Platform = nomad
Deployment Status
ID = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Version = v0
Status = successful
Description = Deployment completed successfully
Instances = 1 desired, 1 placed, 1 healthy, 0 unhealthy
Instances
ID PROCESS VERSION REGION DESIRED STATUS HEALTH CHECKS RESTARTS CREATED
XXXXXXXX app 0 nrt run running 1 total, 1 passing 0 1m31s ago
-
特に、
Hostname = myflyapp.fly.dev
より、外部のブラウザから Python Flask アプリ myflyapp にアクセスするための URL が http://myflyapp.fly.dev/ であることが確認できる -
また、
flyctl open
を実行すると作業環境は GUI が無いのでブラウザの起動に失敗しますが、表示されるメッセージからもアクセスする URL を確認することができる$ flyctl open opening http://myflyapp.fly.dev ... Error failed opening http://myflyapp.fly.dev: exec: "xdg-open": executable file not found in $PATH
ブラウザで http://myflyapp.fly.dev/ でアクセス
- myflyapp アプリが mydbapp から取得した
Hello World
が表示されれば正常に動作しています。