2024年7月17日水曜日

EC2上でAPIを定期的に動かしRDSに格納とFlaskでの可視化

 EC2上でAPIを定期的に動かして取得したデータをRDSに保存します。またそのデータをFlaskでWebアプリとして表示していきます。

(1) APIの動作確認

利用するAPIは、International Space Station Current Location (http://open-notify.org/Open-Notify-API/ISS-Location-Now/) で国際宇宙ステーションの現在位置を返してくれます。まずPostmanで実行してみます。指定されたURLを入力するだけです。

Pythonコードへ変換すると、次のようになります。
import requests
url = "http://api.open-notify.org/iss-now.json"
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
view raw gistfile1.txt hosted with ❤ by GitHub
APIの動作確認ができました。もう少し、キレイなコードにして、時刻も秒までわかりやすく表示します。
import requests
import datetime
import pytz
url = "http://api.open-notify.org/iss-now.json"
response = requests.get(url)
data = response.json()
# データの抽出
timestamp = data['timestamp']
latitude = data['iss_position']['latitude']
longitude = data['iss_position']['longitude']
# timestampをUTCからJSTに変換
utc_time = datetime.datetime.utcfromtimestamp(timestamp)
jst = pytz.timezone('Asia/Tokyo')
jst_time = utc_time.replace(tzinfo=pytz.utc).astimezone(jst)
jst_date_time = jst_time.strftime('%Y-%m-%d %H:%M:%S')
# 結果を表示
print(f"Date and Time (JST): {jst_date_time}")
print(f"Latitude: {latitude}")
print(f"Longitude: {longitude}")
view raw gistfile1.txt hosted with ❤ by GitHub

(2) EC2上で取得できるようにする
次にEC2上でAPIを取得できるようにします。
EC2の立ち上げは下記を参照してください。
・EC2でサーバを立ち上げてクラウドシェルで接続 (https://smizunolab.blogspot.com/2023/10/ec2.html)
今回は「ec2-user」のままで実施します。pythonはインストールされていますがpipがありませんので、pipをインストールしてください。
$ sudo yum install python3-pip
Pythonファイルを作成して、(1)で実施したプログラムを動かしてみます。ファイル名は「iss_api.py」とします。
$ python3 iss_api.py

(3) データベースの用意
今回はAWS RDSをCloud Shellから使います。下記を参考にしてください。
AWS RDSでデータベースを作成しCloudShellから接続してSQL文からデータを格納する (https://smizunolab.blogspot.com/2024/06/aws-rdscloudshellsql.html)
同じEC2上で、MySQLを動かして実施することも可能です。その場合は下記を参考にしてください。
EC2でMySQL のインストール (https://smizunolab.blogspot.com/2023/11/ec2mysql.html)
MySQLで実施する場合、GPG-KEYの更新を忘れないようにしてください。
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023

データベース、テーブルの作成をしていきます。
データベース名:iss、テーブル名:point で作成します。
-- データベースを作成
CREATE DATABASE iss;
-- データベースを使用
USE iss;
-- テーブルを作成
CREATE TABLE point (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
latitude FLOAT(10, 6) NOT NULL,
longitude FLOAT(10, 6) NOT NULL
);
view raw gistfile1.txt hosted with ❤ by GitHub
RDS(またはMySQL)に接続し、SQL文を発行します。

(4) EC2上のPythonプログラムからデータベースへ情報の格納
次に国際宇宙ステーションのAPIを使って情報を取得し、データベースに格納していきます。下記を参考にしてください。
・Pythonでデータベースに情報を格納する (https://smizunolab.blogspot.com/2024/07/python.html)
MySQL Connectorのインストール:pip install mysql-connector-python
MySQL Connectorのインストールを行い、次のプログラムを実行します。プログラム名は「iss_api_db.py」とします。MySQLのhostはRDSを使っている場合は、エンドポイントを利用します。MySQLをEC2上にインストールした場合は、localhostのままです。
import requests
import datetime
import pytz
import mysql.connector
# ISSの現在位置を取得
url = "http://api.open-notify.org/iss-now.json"
response = requests.get(url)
data = response.json()
# データの抽出
timestamp = data['timestamp']
latitude = data['iss_position']['latitude']
longitude = data['iss_position']['longitude']
# timestampをUTCからJSTに変換
utc_time = datetime.datetime.utcfromtimestamp(timestamp)
jst = pytz.timezone('Asia/Tokyo')
jst_time = utc_time.replace(tzinfo=pytz.utc).astimezone(jst)
jst_date_time = jst_time.strftime('%Y-%m-%d %H:%M:%S')
# MySQLデータベースに接続
connection = mysql.connector.connect(
host="localhost",
user="your_username",
password="your_password",
database="iss"
)
cursor = connection.cursor()
# データの挿入
insert_query = """
INSERT INTO point (timestamp, latitude, longitude)
VALUES (%s, %s, %s)
"""
cursor.execute(insert_query, (jst_date_time, latitude, longitude))
connection.commit()
cursor.close()
connection.close()
print("Data inserted successfully.")
view raw gistfile1.txt hosted with ❤ by GitHub
$ python3 iss_api_db.py
select文でテーブルを確認すると、データが格納されているのがわかります。

(5) プログラムの定期実行
cronを使って、1分に1回定期実行していきます。まず、cronieをインストールします。
$ sudo yum install cronie
$ sudo systemctl start crond
$ sudo systemctl enable crond
ターミナルでcrontab -eコマンドを実行し、crontabの編集を開始します。
$ crontab -e
書き込む内容
* * * * * /usr/bin/python3 /home/ec2-user/iss_api_db.py 

$ chmod +x iss_api_db.py 
データベースを確認すると1分毎格納されていることがわかります。

(6) Flaskを使って、最新5件のpoint情報を地図上に表示する
Flaskを使って、pointテーブルにある最新5件の情報を地図上に表示します。
参考:EC2にRDSから情報を取得し、FlaskでWebアプリとして表示させる (https://smizunolab.blogspot.com/2024/07/ec2rdsflaskweb.html)
Flaskのインストール
$ pip install Flask
上記の参考リンクと同様に以下のようにディレクトリ構成を作成します。
iss_app/
   ├── app.py
   └── templates/
                  └── index.html
app.py
from flask import Flask, render_template
import mysql.connector
app = Flask(__name__)
def get_db_connection():
conn = mysql.connector.connect(
host="localhost",
user="your_username",
passwd="your_password",
database="iss"
)
return conn
@app.route('/')
def index():
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM point ORDER BY timestamp DESC LIMIT 5")
points = cursor.fetchall()
cursor.close()
conn.close()
return render_template('index.html', points=points)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
view raw gistfile1.txt hosted with ❤ by GitHub


index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ISS Latest Positions</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<style>
#map {
height: 600px;
width: 100%;
}
</style>
</head>
<body>
<h1>ISS Latest Positions</h1>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script>
var map = L.map('map').setView([0, 0], 2); // 初期表示の中心を設定
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
}).addTo(map);
// 最新のポイントデータを取得してマーカーを配置
var points = {{ points | tojson }};
points.forEach(function(point) {
var marker = L.marker([point.latitude, point.longitude]).addTo(map);
marker.bindPopup("<b>Timestamp:</b> " + point.timestamp + "<br><b>Latitude:</b> " + point.latitude + "<br><b>Longitude:</b> " + point.longitude);
});
</script>
</body>
</html>
view raw gistfile1.txt hosted with ❤ by GitHub


注意:
Flaskはデフォルトポート5000なので、セキュリティグループからインバウンド設定に5000ポートを許可する。

$ python3 app.py
今回はテスト用なので「http」でアクセスしています。ブラウザによっては、デフォルトが「https」なので接続時にエラーになる可能性もあります。

ここまでの内容で、APIで定期的に取得した内容をDBに格納し、それをWebアプリとして表示することができました。

注意:
cronに関しては、
$ crontab -e
で開き、対象行を削除するか、コメントアウト(#をつける)をしてください。

2024年7月10日水曜日

EC2にRDSから情報を取得し、FlaskでWebアプリとして表示させる

 今回はEC2にRDSから情報を取得し、FlaskでWebアプリとして表示させることをやっていきます。

事前知識

・EC2でサーバを立ち上げてクラウドシェルで接続(https://smizunolab.blogspot.com/2023/10/ec2.html)

・Pythonでデータベースに情報を格納する(https://smizunolab.blogspot.com/2024/07/python.html)

・EC2でjupter-notebookをインストールする(https://smizunolab.blogspot.com/2023/11/ec2jupter-notebook.html)

1. EC2を立ち上げ、Flaskをインストールする

上記の「EC2でサーバを立ち上げてクラウドシェルで接続」の方法でEC2にサーバを立ち上げます。また「EC2でjupter-notebookをインストールする」にあるpipのインストールを実施しといてください。

$ sudo yum install python3-pip

今回はec2-userでFlaskをインストールしていきます。

(1) Flaskのインストール

$ pip install Flask mysql-connector-python

(2) Flaskアプリケーションの設置 : ホームディレクトリに作成

$ mkdir my_flask_app

$ cd my_flask_app

$ touch app.py

$ mkdir templates

$ touch templates/index.html

(3) app.pyの作成:

from flask import Flask, render_template
import mysql.connector
app = Flask(__name__)
def get_db_connection():
conn = mysql.connector.connect(
host="localhost",
user="root",
passwd="LinuxOS99@",
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')
view raw gistfile1.txt hosted with ❤ by GitHub

ホストやパスワードは自分の環境に揃える

(4) index.htmlの作成:

<!doctype html>
<html lang="en">
<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>
<img src="{{ apod.url }}" alt="{{ apod.title }}">
<p>{{ apod.explanation }}</p>
</div>
{% endfor %}
</body>
</html>
view raw gistfile1.txt hosted with ❤ by GitHub

templates/index.html にコードを書き込みます。

(5) セキュリティグループの追加 : インバウンドルールに5000を追加(Flaskのデフォルトポートが5000)

(6) app.pyの実行 : python3 app.py
(7) http://publicDNS:5000 でアクセスし、データベースの内容を確認する

もしうまくいかない場合は以下をチェック
・データベース(今回はRDS)に接続ができているか
・データベース上にデータベース(nasa)、テーブル(apod)が保存されているか
・Flaskのインストールができているか
・5000番ポートでのアクセスができているか(セキュリティグループにインバウンドルールが追加されているか)

2024年7月8日月曜日

CentOS9にOpenMPI(Python: mpi4py) + OpenMP(C言語: gcc)を動かす

 CentOS9上にOpenMPI(Python: mpi4py) + OpenMP(C言語: gcc)を動かしていきます。MPIでプロセスを生成して、その各プロセスでMP(C言語)を呼び出し、その中でスレッドを動かします。

1. 必要なモジュールのインストール(root権限で実施)

CentOS9にはPythonはインストール済み -> Python 3.9.19

    (1) pipをインストール

$ sudo yum install python3-pip 

    (2) Python開発モジュールをインストール(これがないとmpi4pyがうまくインストールできなかった)

$ sudo yum install python3-devel 

    (3) gccをインストール

$ sudo yum install gcc

    (4)OpenMPIをインストール+パスの追加

$ sudo yum install openmpi

$ echo 'export PATH=/usr/lib64/openmpi/bin:$PATH' >> ~/.bashrc

$ echo 'export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH' >> ~/.bashrc

$ source ~/.bashrc

2. MPのテスト(ユーザ権限で実施)
OpenMPのテストをします。「omp_program.c」を作成します。
#include <stdio.h>
#include <omp.h>
void openmp_function() {
#pragma omp parallel
{
int thread_id = omp_get_thread_num();
int num_threads = omp_get_num_threads();
printf("Hello from thread %d out of %d threads\n", thread_id, num_threads);
}
}
int main() {
openmp_function();
return 0;
}
view raw gistfile1.txt hosted with ❤ by GitHub
    (1) コンパイル
$ gcc -fopenmp -o omp_program omp_program.c
    (2) 実行
$ ./omp_program
3. mpi4pyをインストール(ユーザ権限で実施)
    (1) mpi4pyをインストール
$ pip3 install mpi4py
    (2) 「run_mpi.py」を作成
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
print(f"Hello from rank {rank}")
view raw gistfile1.txt hosted with ❤ by GitHub
    (3) インターフェースの設定
さくらサーバの場合警告が出たので、以下を設定してから実行
export PSM3_ALLOW_LOC_ALIASES=1
export PSM3_ENABLE_SYMMAP=0
export PSM3_NIC=ens3
export OMPI_MCA_mtl_ofi_provider_exclude="psm3"
    (4) MPIの実行
mpirun -np 4 python3 run_mpi.py
4. MPをmpi4pyから呼び出して、プロセス2、スレッド4で動かす
MPIプログラム(Python)からrankを引数として、MP(C言語)を呼び出し、プロセス数2、スレッド数4、計 2 * 4 = 8並列で動かします。
    (1) [omp_program_with_rank.c]の作成
#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
void openmp_function(int rank) {
#pragma omp parallel
{
int thread_id = omp_get_thread_num();
int num_threads = omp_get_num_threads();
printf("Rank %d, Hello from thread %d out of %d threads\n", rank, thread_id, num_threads);
}
}
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <rank>\n", argv[0]);
return 1;
}
int rank = atoi(argv[1]);
openmp_function(rank);
return 0;
}
view raw gistfile1.txt hosted with ❤ by GitHub
MPの動作確認

gcc -fopenmp -o omp_program_with_rank omp_program_with_rank.c

./omp_program_with_rank 0

Rank 0, Hello from thread 0 out of 6 threads

Rank 0, Hello from thread 3 out of 6 threads

Rank 0, Hello from thread 1 out of 6 threads

Rank 0, Hello from thread 4 out of 6 threads

Rank 0, Hello from thread 2 out of 6 threads

Rank 0, Hello from thread 5 out of 6 threads

    (2) [run_mpi_with_rank.py]の作成

from mpi4py import MPI
import subprocess
def run_c_program(rank):
# 環境変数を設定してスレッド数を指定
env = {"OMP_NUM_THREADS": "4"}
command = ["./omp_program_with_rank", str(rank)]
result = subprocess.run(command, env=env)
return result.returncode
if __name__ == "__main__":
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
if rank < 2: # 2プロセスのみ実行
print(f"Running on rank {rank} out of {size} processors")
run_c_program(rank)
view raw gistfile1.txt hosted with ❤ by GitHub
実行
mpirun -np 2 python3 run_mpi_with_rank.py   
Running on rank 0 out of 2 processors
Running on rank 1 out of 2 processors
Rank 0, Hello from thread 0 out of 4 threads
Rank 0, Hello from thread 3 out of 4 threads
Rank 0, Hello from thread 2 out of 4 threads
Rank 0, Hello from thread 1 out of 4 threads
Rank 1, Hello from thread 0 out of 4 threads
Rank 1, Hello from thread 3 out of 4 threads
Rank 1, Hello from thread 2 out of 4 threads
Rank 1, Hello from thread 1 out of 4 threads

OpenMP(C言語: gcc)でスレッドを動かし、mpi4pyからMPIでプロセスを生成し、rankを引数として、2プロセス、4スレッドで動かすことができました。
5. SQUIDで動かす
OpenMPI + OpenMPは実際大規模並列環境で動かします。今回は大阪大学サイバーメディアセンターSQUIDで動かしてみます。
(1) omp_program_with_rank.c の作成とコンパイル・実行
gcc -fopenmp -o omp_program_with_rank omp_program_with_rank.c
./omp_program_with_rank 0
(2) run_mpi_with_rank.py の作成と実行
run_mpi_with_rank.pyの作成後、ジョブスクリプトを作成します。
[run_mpi_with_rank.sh]
#!/bin/bash
#------- qsub option -----------
#PBS -q SQUID
#PBS --group=GXXXXX
#PBS -m eab
#PBS -M mizuno@mail.address.jp
#PBS -l cpunum_job=76
#PBS -l elapstim_req=00:01:00
#PBS -l memsz_job=248GB
#PBS -b 1
#------- Program execution -----------
module load BasePy/2021
module load BaseCPU
source /sqfs/work/GXXXXX/vXXXXXX/test-env/bin/activate
cd $PBS_O_WORKDIR
mpiexec -np 8 python3 run_mpi_with_rank.py > run_mpi_with_rank.txt
view raw gistfile1.txt hosted with ❤ by GitHub
qsub run_mpi_with_rank.sh
作成されたrun_mpi_with_rank.txtを確認すると動いています。これでSQUIDでも実行できました。

2024年7月5日金曜日

Pythonでデータベースに情報を格納する

 今回はPythonからデータベースにアクセスし情報を格納していく部分を実施していきます。これができると得られた情報をDBに格納したり、取り出したりとデータの処理がしやすくなります。今回はAWS RDSを使っていきます。今回の内容を実施するにあたり、予備知識として参考になるのが以下になります。

[データベース参考]

・データベースの正規化(エクセルでの処理)(https://smizunolab.blogspot.com/2024/06/blog-post_18.html)

・AWS RDSでデータベースを作成しCloudShellから接続してSQL文からデータを格納する(https://smizunolab.blogspot.com/2024/06/aws-rdscloudshellsql.html)

・データベースへcsvでデータをインポートする(https://smizunolab.blogspot.com/2024/06/csv.html)

[API参考]

・API (Application Programming Interface)の利用:POSTMANの利用(https://smizunolab.blogspot.com/2024/06/api-application-programming.html)

1. AWS RDSの準備

RDSを立ち上げて準備をしておきます。Public Accessが可能な状態にしてください。以下の内容と同じDBを立ち上げておきます。

・AWS RDSでデータベースを作成しCloudShellから接続してSQL文からデータを格納する(https://smizunolab.blogspot.com/2024/06/aws-rdscloudshellsql.html)

2. APIで情報を取得

今回はAPIで情報を取得してそれをDBに格納していきます。APIが初めてな人は下記を参照してください。

・API (Application Programming Interface)の利用:POSTMANの利用(https://smizunolab.blogspot.com/2024/06/api-application-programming.html)

今回は、NASA API:https://api.nasa.gov/ を利用します。各自でアカウント登録をして、APIキーを取得してください(アカウント登録をするとAPIキーが送られてきます)。

このAPIを使って、Pythonで動かしてみます。APIの使用はAPI提供元のサイトを参照してください。APIキーは自分のものを入れてください。
import requests
url = "https://api.nasa.gov/planetary/apod?api_key=xxxxxxxxxxxxxxxxxxxxxxxx"
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
view raw gistfile1.txt hosted with ❤ by GitHub

得られた情報を整理します。以下のソースコードは日付も指定しています。
import requests
import json
from IPython.display import Image, display
url = "https://api.nasa.gov/planetary/apod?api_key=xxxxxxxxxxxxxxxxxxxxx&date=2021-09-01"
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
data = response.json()
# 各キーごとに内容を表示
print("日付:", data["date"])
print("説明:", data["explanation"])
print("HD画像URL:", data["hdurl"])
print("メディアタイプ:", data["media_type"])
print("サービスバージョン:", data["service_version"])
print("タイトル:", data["title"])
print("画像URL:", data["url"])
# 画像URLの取得
image_url = data["url"]
# 画像の表示
display(Image(url=image_url))
view raw gistfile1.txt hosted with ❤ by GitHub
3. データベースの準備
情報をデータベースに格納できるように準備します
(データベース名 : nasa、テーブル : apod)
CREATE DATABASE IF NOT EXISTS nasa;
USE nasa;
CREATE TABLE IF NOT EXISTS apod (
id INT AUTO_INCREMENT PRIMARY KEY,
date DATE NOT NULL,
explanation TEXT,
hdurl VARCHAR(255),
media_type VARCHAR(50),
service_version VARCHAR(50),
title VARCHAR(255),
url VARCHAR(255)
);
view raw gistfile1.txt hosted with ❤ by GitHub
今回のテーブル内容を、APIで得られる値から設定しています。

4. PythonからDBへ情報の格納
Pythonからデータベースに情報を格納していきます。
MySQL Connectorのインストール:pip install mysql-connector-python
Pythonからデータベースに情報を格納します。
import mysql.connector
import requests
# NASA APIからデータを取得
url = "https://api.nasa.gov/planetary/apod?api_key=xxxxxxxxxxxxxxxxxxxxxxx&date=2021-09-01"
response = requests.get(url)
data = response.json()
# MySQLデータベースへの接続
conn = mysql.connector.connect(
host="database-001.xxxxxxxxxxxxx.us-east-1.rds.amazonaws.com",
user="admin",
passwd="LinuxOS99",
database="nasa"
)
# カーソルの作成
cursor = conn.cursor()
# SQL文の定義
sql = """
INSERT INTO apod (date, explanation, hdurl, media_type, service_version, title, url)
VALUES (%s, %s, %s, %s, %s, %s, %s)
"""
# データの挿入
values = (
data["date"],
data["explanation"],
data["hdurl"],
data["media_type"],
data["service_version"],
data["title"],
data["url"]
)
# SQL文の実行
cursor.execute(sql, values)
# コミット(変更をデータベースに反映)
conn.commit()
# 接続のクローズ
cursor.close()
conn.close()
view raw gistfile1.txt hosted with ❤ by GitHub
プログラムを実行したらselect文で情報を確認します。
これでPythonから情報をDBに格納ができました。