Streamlitアプリの公開と運用まとめ|ポート設定・キャッシュ・スリープ対策
/ 7 min read
Table of Contents
はじめに
Streamlitは、Pythonのスクリプトをそのままブラウザで動くアプリにできるライブラリです。手元で動かすのは簡単ですが、公開して運用しようとすると、ポートやキャッシュでつまずきます。
実際に引っかかったところをまとめました。
起動とポートの指定
既定では8501番で起動します。
$ streamlit run app.pyポートを変えたい場合は引数で指定します。
$ streamlit run app.py --server.port 8080外部からアクセスさせる場合は、待ち受けるアドレスも指定します。
$ streamlit run app.py --server.address 0.0.0.0 --server.port 8080Cloud9で動かす
AWS Cloud9で開発している場合は、8080番で起動してからプレビュー機能を使います。
$ streamlit run app.py --server.port 8080画面上部のツールバーから Preview > Preview Running Application を選ぶと、IDE内のブラウザで確認できます。
80番ポートで公開する
ブラウザからポート番号なしでアクセスさせるには、80番で待ち受ける必要があります。ただし1024番未満のポートは、root権限がないと直接バインドできません。
iptables で転送するのが簡単です。
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 850180番宛の通信を8501番に転送するので、Streamlit自体は通常どおり8501番で起動したままにできます。
キャッシュを使い分ける
Streamlitは、操作のたびにスクリプト全体を上から再実行します。そのため、重い処理はキャッシュしないと毎回走ります。
キャッシュのコマンドは、バージョン1.18.1から次の2つに整理されました。以前の st.cache や st.experimental_memo、st.experimental_singleton は置き換えられています。
| デコレータ | 用途 | 例 |
|---|---|---|
@st.cache_data |
データを返す処理 | DataFrameの読み込み、API取得、計算結果 |
@st.cache_resource |
接続やオブジェクト | DB接続、機械学習モデルのロード |
判断の基準は、戻り値をコピーして返してよいかどうかです。データはコピーして問題ありませんが、DB接続をコピーすると別物になってしまうため、cache_resource で同じオブジェクトを共有します。
import pandas as pdimport streamlit as stfrom sqlalchemy import create_engine
@st.cache_resourcedef get_db_engine(): """DB接続(アプリ全体で1つを共有する)""" url = "sqlite:///hoge.db" return create_engine(url, connect_args={"check_same_thread": False})
@st.cache_datadef get_df(): """取得したデータをキャッシュする""" engine = get_db_engine() return pd.read_sql(sql="SELECT * FROM fugas", con=engine)create_engine は sqlalchemy からのインポートが必要です。
Community Cloudのスリープ対策
Streamlit Community Cloudは無料でアプリをホストできますが、**一定期間アクセスがないとスリープします。**次に開いた人は起動待ちの画面を見ることになります。
定期的にアクセスして起こしておく方法があります。
$ pip install selenium chromedriver-binary-autofrom time import sleep
import chromedriver_binary # noqa: F401 # インポートするだけでパスが通るfrom selenium import webdriver
def web_open(url: str) -> None: options = webdriver.ChromeOptions() options.add_argument("--headless")
driver = webdriver.Chrome(options=options) try: driver.implicitly_wait(120) driver.get(url) sleep(30) # 起動を待つ finally: driver.quit() # 必ず閉じるこれをcronで定期実行します。cronからの実行方法はPythonのvenvとpipまとめに書いています。
# 6時間おきにアクセスする0 */6 * * * cd /home/ubuntu/app; /home/ubuntu/app/venv/bin/python wake.py >> /tmp/wake.log 2>&1おわりに
Streamlitは書きやすい反面、「スクリプト全体が毎回再実行される」という前提を知らないと、なぜ遅いのかが分かりません。重い処理を見つけたら、まず cache_data か cache_resource のどちらが適切かを考えてみてください。
公開まわりは、手元で試すなら --server.port、外部に出すならnginxを前段に置く、と切り分けておくと迷いません。
