今回は、RDSに格納したNASAのAPOD(Astronomy Picture of the Day)のデータを、EC2上のFlaskアプリから取得し、Webページとして80番ポートで公開します。途中でつまずいた点(アクセスできない・HTMLタグがそのまま出る・画像が出ない)も、確認しながら解決していきます。
事前知識
・EC2でサーバを立ち上げてクラウドシェルで接続(https://smizunolab.blogspot.com/2023/10/ec2.html)
・Pythonでデータベースに情報を格納する(https://smizunolab.blogspot.com/2024/07/python.html)
・EC2にRDSから情報を取得し、FlaskでWebアプリとして表示させる(2024/7/10の記事)

完成した画面
全体の構成と作業の流れ
Google Colab(NASA APIから取得してRDSへ格納)→ RDS(MySQL、DB:nasa、テーブル:apod)→ EC2(Flask、ポート80)→ ブラウザ(http://EC2のパブリックDNS)という構成です。ブラウザ→EC2は80番(HTTP)、EC2→RDSは3306番(MySQL)で通信するので、どちらもセキュリティグループでの許可が必要です。
作業の流れ:①RDSを起動 → ②データを確認 → ③EC2を作成 → ④EC2に接続 → ⑤インストール → ⑥ファイル作成 → ⑦起動・確認 → ⑧つまずきの解決
1. RDSの準備とデータの確認
(1) RDSを起動する

RDSコンソールのデータベース一覧
RDSコンソールの「データベース」でdatabase-3を確認します。ステータスが「一時的に停止済み」なら、選択して「アクション」→「開始」を押します。起動には数分かかるので、ステータスが「利用可能」になるまで待ちます。
(2) 前回の復習:ColabからAPODを取得してRDSに格納

ColabでAPOD APIからデータを取得するコード
NASAのAPOD APIからデータを取得し、RDSのnasaデータベースのapodテーブルにINSERTしています(詳しくは「Pythonでデータベースに情報を格納する」を参照)。今回はこのデータをFlaskで表示します。
(3) CloudShellからRDSのデータを確認する
$ mysql -h (RDSのエンドポイント) -P 3306 --ssl-ca /certs/global-bundle.pem --ssl-verify-server-cert -u admin -p MySQL> use nasa; MySQL> show tables; MySQL> SELECT date, title FROM apod;

apodテーブルがあることを確認
2. EC2を作成して接続する
(1) インスタンスを起動:名前・AMI・タイプ

インスタンスを起動の画面
EC2の「インスタンスを起動」から、次のように設定します。
・名前:flask-app-01(任意)
・AMI:Amazon Linux 2023
・インスタンスタイプ:t3.micro(無料利用枠)
ユーザー名は ec2-user になります。
(2) キーペアを作成する

キーペアの作成
「新しいキーペアの作成」でキーペア名を入力し(例:flask-app-01)、タイプはRSA、形式は.pemにします。ダウンロードした.pemは安全な場所に保存します。
(3) ネットワーク設定:HTTPを許可する

ネットワーク設定(SSH・HTTPS・HTTPを許可)
「セキュリティグループを作成」を選び、SSH・HTTPS・HTTPのトラフィックを許可します。80番で公開するのでHTTPのチェックは必須です。ストレージは8GiBのままで「インスタンスを起動」します。
(4) EC2 Instance Connectで接続する

インスタンスに接続の画面
インスタンスを選択して「接続」→「EC2 Instance Connect」タブで、ユーザー名 ec2-user のまま「接続」します。ブラウザ上にターミナルが開き、Amazon Linux 2023のロゴが表示されれば接続完了です。
3. Flaskアプリを作る
(1) pip・Flask・MySQLコネクタをインストール
$ sudo yum install python3-pip (Is this ok [y/N]: と聞かれたら y) $ sudo pip install Flask mysql-connector-python

python3-pipのインストール
(2) アプリのフォルダとファイルを作る:ホームディレクトリに作成
$ mkdir my_flask_app $ cd my_flask_app $ touch app.py $ mkdir templates $ touch templates/index.html $ ls -l
できあがる構成は次のとおりです。
my_flask_app/
├── app.py
└── templates/
└── index.html(3) app.pyの作成:RDSからデータを取得してHTMLに渡す
vi app.py で次のコードを書き込みます。
from flask import Flask, render_template
import mysql.connector
app = Flask(__name__)
def get_db_connection():
conn = mysql.connector.connect(
host="(RDSのエンドポイント)",
user="admin",
passwd="(自分のパスワード)",
database="nasa"
)
return conn
@app.route('/')
def index():
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM apod")
apod_data = cursor.fetchall()
cursor.close()
conn.close()
return render_template('index.html', apod_data=apod_data)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=80)・host:RDSのエンドポイントを指定します(localhostではありません)。ホストやパスワードは自分の環境に揃えてください。
・dictionary=True で、列名(titleなど)で値を取り出せます。
・render_template で index.html に apod_data を渡します。
・port=80 で起動するので、URLにポート番号を付ける必要がありません。
(4) index.htmlの作成:データを並べて表示する
vi templates/index.html で次のコードを書き込みます(完成版)。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>APOD</title>
</head>
<body>
<h1>Astronomy Picture of the Day</h1>
{% for apod in apod_data %}
<div>
<h2>{{ apod.title }}</h2>
<p>{{ apod.date }}</p>
{% if apod.media_type == 'image' %}
<a href="{{ apod.url }}"><img src="{{ apod.hdurl }}" alt="{{ apod.title }}" width="600"></a>
{% else %}
<p><a href="{{ apod.url }}">NASAの記事ページで見る</a></p>
{% endif %}
<div>{{ apod.explanation | safe }}</div>
</div>
<hr>
{% endfor %}
</body>
</html>・{% for %} で1件ずつ繰り返し、{{ apod.title }} で列の値を表示します。
・画像は hdurl を使い、クリックすると記事ページ(url)が開きます。
・| safe で説明文に含まれるHTMLを有効にします。
・width="600" で画像がはみ出さないようにします。
(5) アプリを起動してブラウザで確認する
$ sudo python3 app.py * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:80 (止めるときは Ctrl + C)
ブラウザで http://(パブリック IPv4 DNS) を開きます(例:http://ec2-xx-xx-xx-xx.compute-1.amazonaws.com)。
・パブリック IPv4 DNS(またはパブリックIP)は、EC2のインスタンス詳細で確認できます。
・80番なので「:80」は付けなくて大丈夫です。
・証明書を設定していないので、https:// ではなく http:// で開きます。
4. つまずきを確認しながら直す
つまずき①:「このサイトにアクセスできません」

ERR_CONNECTION_TIMED_OUT(応答なし)

セキュリティグループのインバウンドルールを編集
つまずき②:HTMLタグがそのまま表示される

説明文のHTMLタグが文字のまま表示される
explanationに<a>や<strong>などのHTMLが含まれていますが、Jinja2は安全のため {{ }} の中身を文字として表示(エスケープ)します。
<!-- 変更前 -->
<p>{{ apod.explanation }}</p>
<!-- 変更後 -->
<div>{{ apod.explanation | safe }}</div>つまずき③:画像が表示されない

url列とhdurl列の中身を確認
タイトルの横に「壊れた画像」が出るのは、<img>に渡したURLで画像を取得できていないためです。データベースを確認すると、url列は記事ページ(science.nasa.gov/image-article/...)で、画像ファイルはhdurl列に入っていました。
MySQL> SELECT date, url, hdurl FROM apod ORDER BY date DESC;
<img src="{{ apod.hdurl }}" width="600">完成

RDSのデータがWebページに表示された
タイトル・日付・画像・説明文(リンク付き)が表示されました。データはRDSから毎回読み出しているので、Colabで新しい日のデータを追加すれば、ページを再読み込みするだけで反映されます。
まとめと注意点
今日やったこと:RDSを起動してデータを確認 → EC2を作成(HTTPを許可) → Flask+MySQLコネクタを sudo pip で導入 → app.pyとindex.htmlを作成 → sudo python3 app.py で80番公開
確認のポイント:アクセスできない → セキュリティグループとポート/タグが出る → | safe/画像が出ない → urlとhdurlの中身。うまくいかないときは、DBの中身とターミナルのログを見ます。
0 件のコメント:
コメントを投稿