AWS上にStreamlitの動作環境を作ってみた

## 概要
[**Streamlit**](https://streamlit.io/) という Python のWEBアプリケーションフレームワークを動かすための環境をAWS上に作ったのですが、とにかく詰まりに詰まったので覚えている範囲で記録を残しておこうと思います。誰かしらのお役に立てば幸いです。

ということでいってみましょう。

## EC2環境
- Amazon Linux 2
- Apache 2.4.54
- Python 3.7(Amazon Linux 2 に最初から入っていた)

## インフラ構成

大まかにはこんなかんじですが特に凝ったことはしていません。
アベイラビリティゾーンやパブリックサブネットは実際は複数ありますが、本筋にあまり関係ないのでここでは省略してます。

![aws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70638/332c4287-d59d-7fb8-fe8a-79d95b5a6a0e.png)


唯一の注意点として、 <font color="red">**ロードバランサーを置く場合は必ず Application Load Balancer を使ってください。**</font>

理由は、Streamlit はページ上に表示するメインコンテンツは WebSocket で取得しているのですが、**WebSocketに対応しているロードバランサーが Application Load Blancer のみ** であるためです。

ちなみに私はそこに気が付かず、ずっと Classic Load Balancer を使い続けていて、ページは表示するのにずっと `Please wait` 表示でコンテンツが出ないことに何日もハマっていました。いろいろ切り分けして、ロードバランサー側の問題というところに辿り着いたあと、下記の記事を見つけて「そもそもクラシックロードバランサーだとだめなのでは？」という考えに至った次第です。

https://uorat.hatenablog.com/entry/2016/09/26/070000

## EC2側の設定
単に私がそこそこ慣れているというだけの理由で今回は **Apache 2.4** でWEBサーバーを立てています。簡単に設定内容を記載します。

```sh
# Apacheインストール
$ sudo yum install httpd
# バーチャルホスト追加
$ sudo vim /etc/httpd/conf.d/streamlit_app.conf
# Apache起動
$ sudo systemctl start httpd
# 自動起動有効化
$ sudo systemctl enable httpd
```

confの内容は以下のとおりです。
ロードバランサー側で 443 → 80 への転送を行っているため、EC2側では80番の設定のみ記載しています。なお、ドメインとドキュメントルートは必要に応じて読み替えてください。

```conf:/etc/httpd/conf.d/streamlit_app.conf
<VirtualHost *:80>
  ServerName yourdomain.net
  DocumentRoot /var/www/your_app/

  # Streamlit のアプリケーションが8501ポートで待ち受けているため
  # そっちに転送するための設定です
  # WebSocket通信を行う際のリクエスト先が /stream であるため、
  # /stream のみ ws に変更し、それ以外は http のまま転送します
  ProxyPreserveHost On
  ProxyPass / http://localhost:8501/
  ProxyPassReverse / http://localhost:8501/
  <Location "/stream">
    ProxyPass ws://localhost:8501/stream
    ProxyPassReverse ws://localhost:8501/stream
  </Location>
</VirtualHost>
```


## Streamlitの設定
Amazon Linux 2 環境では最初から Python 3.7 と pip3 が使えましたので Python のインストール周りについては省略します。

### インストール

```sh
$ pip3 install streamlit
$ pip3 install plotly_express
```

streamlit だけをインストールしても
`ModuleNotFoundError: No module named 'plotly_express'` と怒られてしまい、公式のデモアプリすら動かないので `plotly_express` もインストールしておきましょう。

### 設定ファイルの追加
必須ではありませんが、コマンドラインオプションの指定が面倒な方は作っておくと楽です。
`~/.streamlit/config.toml` を作っておくと streamlit コマンド実行時に勝手に指定されます。

```toml:~/.streamlit/config.toml
[server]
# streamlit側のリスナーポート
# デフォルトと同値ですが環境によって8502になっていたりする事例があるので一応明示的に指定しています
port = 8501 

# サーバー側のファイル変更時に即座に反映させます
# 毎回手動で run しなおすのは面倒なので特に理由がなければ指定しておくといいと思います
runOnSave = true 

# これがないとws通信時に一部のヘッダが削除されるようで、
# それが原因で今回の環境下ではコンテンツの取得に失敗していましたため追加しています
enableWebsocketCompression=false

[browser]
# 統計情報の送信をオプトアウトする設定です
gatherUsageStats = false
```

他の設定項目について知りたい方は公式サイトを参考にしてください。

https://docs.streamlit.io/library/advanced-features/configuration

## 公開
単純に `streamlit run %your_app_entrypoint.py%` を実行すれば公開されますが、そのままだとサーバーからログアウトできないので、公式フォーラムに載っていた以下の方法を採用して、tmuxで別セッションを立ち上げて公開しています。

https://towardsdatascience.com/how-to-deploy-a-streamlit-app-using-an-amazon-free-ec2-instance-416a41f69dc3

```sh
# tmux のインストール
$ sudo yum install -y tmux
# streamlit の公開に使うセッションを作成
$ tmux new -s StreamSession
# アプリ公開(helloworld.pyの部分は読み替えてください)
$ streamlit run helloworld.py
```

ページが表示できれば完了です。おつかれさまでした:relaxed:

![st_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70638/f3a4ffe0-cdc4-988f-cbe3-94767dcc3255.png)

## おまけ
streamlit 公開時に `Please wait` がずっと表示され続けていて困っている例はけっこう多いみたいです。例にもれず私も同じ問題で詰まりました。原因はいろいろあると思いますが、主に以下のあたりを疑うと解決するかもしれません。

- [公式が挙げているよくある原因](https://docs.streamlit.io/knowledge-base/deploy/remote-start) に該当している(詳しくはリンク先)
- Apache, Nginx の転送ミスをしている(特に /stream の転送ができているかどうか)
- Classic Load Balancer (CLB) を使っている(私だけ？)

## お世話になった参考リンク様
- [Streamlit Deployment Guide \(wiki\) \- Deploying Streamlit \- Streamlit](https://discuss.streamlit.io/t/streamlit-deployment-guide-wiki/5099)
- [How to Deploy a Streamlit App using an Amazon Free ec2 instance? \| by Rahul Agarwal \| Towards Data Science](https://towardsdatascience.com/how-to-deploy-a-streamlit-app-using-an-amazon-free-ec2-instance-416a41f69dc3)
- [Configuring Apache 2\.4 for proxy \- Using Streamlit \- Streamlit](https://discuss.streamlit.io/t/configuring-apache-2-4-for-proxy/1866)
- [App is not loading when running remotely \- Streamlit Docs](https://docs.streamlit.io/knowledge-base/deploy/remote-start)
- [【新機能】新しいロードバランサー Application Load Balancer（ALB）が発表されました \| DevelopersIO](https://dev.classmethod.jp/articles/alb-application-load-balancer/)