0
0

※現在進行中※【移行・設計】almalinuxへのkubernetes導入Tips(k3d編③) アプリ実装その1

Last updated at Posted at 2024-06-16

目的

  • 「QOL向上」を目的として旧LAMPサーバを構築してきました。
  • 収支管理、健康管理目的で使用しておりますが、新機能を今後追加していくため、機能が膨大になり管理できなくなる懸念があるため、今までの機能も見直しを進めていきます。
  • また、旧LAMPサーバは、定期的なセキュリティバージョンアップとモジュール管理ができてないため、下記2点を
    • コンテナイメージを作成・定期的なバージョンアップをする。
    • k3dベースへのアーキテクチャシフトする
    • モジュール管理として、Github(プライベートリポジトリ)で構成管理する
  • 旧LAMPサーバでは、BIツールでJavaVMのメモリを多く使用し、OSがフリーズする事象が発生していました。
  • 対策として、BIツールをCPU負荷をメインとしたpythonベースのアプリケーションに変更します。(JavaよりもPythonのほうが学習難易度が低く、データ分析やAI、機械学習と幅広く使われているで、学習範囲を広げるという狙いもあります。)
  • PCのディスク使用量削減のため、新LAMPサーバ旧Kubernetesサーバは廃止し、k3dサーバを搭載したサーバに統合します。(下図イメージ)
  • アーキテクチャ変更により、詳細な設計・実装を行います

前提

  • 本来、要件整理してからアーキテクチャ構築を進めるべきですが、動作検証を優先するため、アーキテクチャ構築をしています。
  • k3dクラスタベースの下記アーキテクチャを構築しております。
    • ロードバランサ(k3dベース)
    • Nginx(Deployment)
    • Python(Deployment)
    • MySQL(Deployment)
  • 上記アーキテクチャを構築した前提で、アプリケーションの実装まで検討します。

システム化の方針

  • 要件定義以前に、そもそも何を実現していくか
  • まず、今までの構築したLAMPサーバとして、何が必要か不要かを判断し、システム化の方針を立てます

誰に対しての開発か

  • 自分のみならず、妻、家族

目的の再確認

  • 目的は「QOL向上」ですが、もう少し分類します。
    • スキルや趣味の開発(個人スキル向上)
    • 健康への配慮(睡眠時間、メンタルケア)
    • 家族のコミュニケーション向上(アルバム作成)
    • 時間の管理(優先順位付けの向上)

システム化の方針の整理

  • 具体的にすると以下の4つで、個人開発としてどういう技術でアプローチしていくかを考えます
  • 整理した結果、下記3つは必要なので、
    • スキルや趣味の開発(個人スキル向上)
    • 健康への配慮(睡眠時間、メンタルケア)
    • 家族のコミュニケーション向上(アルバム作成)

スキルや趣味の開発(重要度:★★★)

  • 目標:個人のスキルアップ
  • 現状:個人開発での、技術要件定義スキル強化が乏しい
  • 課題:マネジメント業務が重きとなっており、業務でのモダンな技術経験が希薄
  • 解決策:個人でのアプリ開発向上、データ分析、運用スキルの向上
  • 期待値①:アプリ開発・運用管理での上記機能が使いこなせること。
  • 期待値②:新規機能追加(写真管理のアルバム管理+旅行・レシピなどの提案企画支援)
  • 使用技術:既に利用している下記を利用
    • Nginx(html、css、javascript)
    • python(Flask、FastAPI、SQLAlchemy、Pandas)
    • MySQL
    • k3d
    • Github(構成管理、スケジュール管理)

健康への配慮(重要度:★★☆)

  • 目標:メンタルヘルスのケア、リラックスする時間を確保
  • 現状:勤怠管理から勤務時間を割り出したり、iPhoneの行動記録やジムでのトレーニング実績の分析は可能
  • 課題:責任世代へ変わることで、個人および妻のメンタルケアが必要不可欠。
  • 解決策:メンタルヘルスの情報サイト一覧作成、睡眠・トレーニング記録管理
  • 期待値①:ジム通いを週2~3は維持できること。
  • 期待値②個人開発にかける時間を減らし、睡眠・趣味の時間に充てられること。
  • 使用技術:既に利用している下記を利用
    • Nginx(html、css、javascript)
    • python(Flask、FastAPI、SQLAlchemy、Pandas、Plotly)
    • MySQL
    • k3d
    • Github(スケジュール管理)

家族のコミュニケーション向上(重要度:★★★)

  • 目標:家族との団欒を増やす
  • 現状:家族とのコミュニケーションを維持できてない
  • 課題:現状の通り
  • 解決策:維持できるためのツール導入(アルバム作成)
  • 期待値:家族とのコミュニケーションを支援するコンテンツ作成
  • 役割:アルバム、データ分析などを通じたつながりの維持支援
  • 使用技術:既に利用している下記を利用
    • Nginx(html、css、javascript)
    • python(Flask、FastAPI、SQLAlchemy、Pandas)
    • MySQL
    • k3d
    • Github(スケジュール管理)

時間の管理(重要度:対象外)

時間管理に関しては、Github(ProjectによるBacklog利用)で事足りるため、
対象から外すことにしました。

  • 目標:時間を有効活用する
  • 現状:メモとかで管理している
  • 課題:プライベートでの時間管理ができてない
  • 解決策:行動履歴と優先順位を立てるとともに、タスクに対して時間記録と分析をする
  • 期待値①:タスク優先順位設定の判断が早まること
  • 期待値②:タスクの時間記録

システム化の方針から機能要件・非機能要件整理 :construction:

機能要件

  • 下記を1つのサーバで行います
    • スキルや趣味の開発(個人スキル向上)
    • 健康への配慮(睡眠時間、メンタルケア)
    • 家族のコミュニケーション向上(アルバム作成)
  • システム化の方針から機能要件を大枠レベルで整理します。
    • 画面設計(画面一覧、画面遷移、画面レイアウト)
      • ログイン機能 :construction:
      • 画面遷移(ログイン→Top画面→各メニューの画面一覧)
      • 収支計算、健康管理の画面
      • アルバムコンテンツ作成 :construction:
      • URL一覧 :construction:
    • API設計(URL一覧、処理内容)
      • 収支計算、健康管理の表、グラフURL
      • アルバムコンテンツのURL :construction:
      • URL一覧 :construction:
    • 外部インターフェース設計(外部サイトとのやりとり、通信フロー)
      • アルバムコンテンツの保管先サーバへの通信 :construction:
      • URL一覧 :construction:
    • DB設計(テーブル定義)
      • 収支計算、健康管理のテーブル定義
    • 処理パターン(オンライン、バッチ)
      • オンライン:スマホからの打鍵
      • バッチ:各種メンテナンス
        • ログバックアップ・削除・圧縮
        • 統計情報収集(k3d、OS、コンテナ)
        • k3d起動・停止、k3dクラスタ再作成
        • :construction: イメージアップデート(難易度高め)
        • データベースエクスポート・インポート

非機能要件

  • AWSのWell Archtected Flameworkの考え方を参考にしていますが、セキュリティ、可用性、性能、メンテナンス、コストの5観点に加え、移植性についての要件になります。
    • セキュリティ要件(アクセス制御、データの暗号化など)
      • ログイン、FlaskAPI、TFA
      • Nginxでのアクセス制御
    • システムの信頼性や可用性(障害時の復旧能力、システムの稼働時間など)
      • k3dの導入(Kubernetesアーキテクチャ環境)
      • ファイルバックアップ
      • 制約:データベースが単一障害点となるため、その考慮点と対策
        • データベースバックアップ・リストア・エクスポート・インポート機能
    • パフォーマンス(応答時間、処理能力など)
      • FlaskAPIとFastAPIベースのPythonフレームワークを利用
    • 運用(メンテナンス)
      • 汎用化するため、Blueprint、オブジェクト指向プログラミングを活用する
      • ソースコードをGithubで管理する
      • k3dの起動停止スクリプトを開発する
      • k3dはバグだらけなので、バグを受容しつつ、回避策用のスクリプトを開発する
    • コスト
      • 無料で使うため、OSSを利用する。
    • 移植性
      • 異なるOSに移行することを考慮し、マニフェストファイルを管理する
      • データエクスポートはmysqldumpコマンド、インポートはmysqlコマンドを利用
  • 使用する技術やプラットフォームの制約
    (ハードウェアの仕様、ソフトウェアのバージョンなど)
    • 仮想マシンは、VMWareを引き続き利用。OSはAlmaLinux8.9を採用する。
  • システムの利用環境に関する要件(言語、地域、ユーザーの文化的背景など)
    • 特にないが、日本語ベースとする。地域も日本。

考慮すること

  • 実テーブルを複数使用する場合、内部結合したビューを作成し、管理します。
  • データベース破損に備え、データベースをファイルエクスポートし、Slaveデータベースへインポートする処理を定期実行します。
  • PythonのCPU、メモリ負荷を減らすため、loggerによるログ出力は必要最小限とします。
  • ブラウザからの接続は秘匿情報の盗聴を防ぐため、https接続のみ許可します。

詳細設計(実装方針・イメージ)

  • pythonでの実装方法を検討します。

モジュール

  • flask,flasksecurity,pandas,sqlalchemy,plotlyが必要そうです
  • plotlyとはデータからグラフ描画・可視化をする機能です。
  • Matplotlibとは違い、plotlyではマウス操作での拡大縮小も可能なのが特徴です。
ライブラリ 用途
Flask ウェブアプリケーションやAPIの開発
SQLAlchemy SQLデータベースとの対話、オブジェクトリレーショナルマッピング(ORM)
plotly グラフの描画や可視化
Pandas 表形式のデータ操作や解析
NumPy 数値計算や配列操作、科学技術計算、データ分析
openai 各種提案※外部APIの利用

ディレクトリ構成

  • 下記役割を分けるようディレクトリ作成し、pythonスクリプトを作成し、配置ています。

    pyscripts
      ├testapp.py(メインスクリプト)
      └content
        ├t_kyn_connection(データベース接続)
        ├t_kyn_models(データモデル定義)
        ├t_kyn_operations(データ操作)
        ├t_kyn_routes(URLとAPI操作とのマッピング)
        └t_kyn_views(表示テンプレート定義)
    
  • データモデル定義

    • pythonのSQLAlchemyのmodelsで定義します(以下は一例)
      from sqlalchemy import Column, Integer, String, DateTime, BigInteger, Numeric, Float, Date
      from sqlalchemy.ext.declarative import declarative_base
      
      Base = declarative_base()
      
      class ActPlanHouserepaircostsTotalView(Base):
          __tablename__ = 'act_plan_houserepaircosts_total_view'
      
          #id = Column(Integer, primary_key=True)
          aphc_year = Column(Integer, primary_key=True)
          aphc_month = Column(Integer, primary_key=True)
          aphc_payment = Column(Numeric)
      
  • データ操作

    • 操作単位で定義します。例えば、データベースのデータをSELECTする場合、下記pandasを利用します(以下は一例)
      import pandas as pd
      from t_kyn_connection.create_session import get_session
      import os
      import logging
      import matplotlib.pyplot as plt
      
      # ログ設定
      logging.basicConfig(level=logging.DEBUG)
      logger = logging.getLogger(__name__)
      
      class DataViewer:
          def __init__(self):
              # セッションを取得
              self.session = get_session()
      
          def view_all_data_as_html(self, table_class):
              try:
                  # 全データを検索
                  logger.debug("データベースからデータを取得します")
                  results = self.session.query(table_class).all()
                  logger.debug(f"取得したデータの数: {len(results)}")
      
                  if results:
                      # 検索結果をデータフレームに変換
                      logger.debug("データフレームに変換します")
                      columns = table_class.__table__.columns.keys()
                      data = [tuple(getattr(result, column) for column in columns) for result in results]
                      df = pd.DataFrame(data, columns=columns)
                      logger.debug("データフレームの内容:\n%s", df)
      
                      # データフレームをHTML形式で表示
                      html_table = df.to_html(index=False)
                      logger.debug("HTML形式のデータ:\n%s", html_table)
      
                      return html_table
                  else:
                      logger.debug("No data found in the table.")
                      return "No data found in the table."
              except Exception as e:
                  logger.error("Error occurred while viewing data: %s", e)
                  return f"Error occurred while viewing data: {e}"
              finally:
                  # セッションをクローズ
                  self.session.close()
      
  • URLとAPI操作とのマッピング・データ表示

    • FlaskのBlueprint, render_templateを用いて、テンプレート用のhtmlファイルを利用し、動的ページを作成します。(以下は一例)
      import sys
      from flask import Blueprint, render_template
      
      # Create a Blueprint
      act_plan_houserepaircosts_total_view_bp = Blueprint('act_plan_houserepaircosts_total_view_bp', __name__)
      act_plan_houserepaircosts_total_view_graph_bp = Blueprint('act_plan_houserepaircosts_total_view_graph_bp', __name__)
      
      sys.path.append('/app')
      @act_plan_houserepaircosts_total_view_bp.get('/')
      def execute_sample_task():
          sys.path.append('/app/content')
      
          from t_kyn_operations.select_args_10desc import DataViewer
          from t_kyn_models.models_t_kyn_act_plan_houserepaircosts_total_view import ActPlanHouserepaircostsTotalView
      
          data_viewer = DataViewer()
      
          html_table = data_viewer.view_all_data_as_html(ActPlanHouserepaircostsTotalView)
      
          return render_template('data.html', table=html_table)
      
  • 表示テンプレート定義

    • data.htmlを定義します。これにより、各テーブルごとにhtmlファイルを用意する必要がなくなります(以下は一例)
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Data Table</title>
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
      </head>
      <body>
          <div class="container mt-5">
              <h1>Data Table</h1>
              <div class="table-responsive">
                  {{ table|safe }}
              </div>
              <div class="mt-3">
                  <button class="btn btn-primary" onclick="goBack()">戻る</button>
              </div>
          </div>
      
          <script>
              // 戻るボタンの関数
              function goBack() {
                  window.history.back();
              }
          </script>
      
      </body>
      </html>
      
      
  • グラフ表示

    • plotlyを用いて、グラフ作成します(以下は一例)
      @act_plan_houserepaircosts_total_view_graph_bp.route('/')
      def execute_sample_plotgraphtask():
          sys.path.append('/app/content')
      
          from t_kyn_operations.select_args_10desc import DataViewer
          from t_kyn_models.models_t_kyn_act_plan_houserepaircosts_total_view import ActPlanHouserepaircostsTotalView
      
          data_viewer = DataViewer()
      
          # データを取得してデータフレームに変換
          df = data_viewer.get_data_as_dataframe(ActPlanHouserepaircostsTotalView)
          graphtitle = ActPlanHouserepaircostsTotalView.__tablename__
      
          if df is not None:
              # グラフをプロット
              data_viewer.plot_data(df, 'aphc_month', 'aphc_payment', graphtitle)
      
          return 'Sample graph plot task executed successfully.'
      
  • データアップロードダウンロード

    • 実装方法を確認中 :construction:
  • データエクスポート、インポート
    MySQLのPodからデータベースをエクスポートし、MasterデータベースではないMYSQLのPodへデータインポートを行います。

    • MySQLダンプファイルをエクスポートするbashスクリプト
    • MySQLダンプファイルからインポートするbashスクリプト
  • ログイン機能

    • 実装方法を確認中 :construction:

改善したい機能

  • 住宅ローンのシミュレーション
    • 1シミュレーション1テーブルとしており、複数のシミュレーション結果をグラフにする際、複数テーブルを結合してました。しかし、結合処理は応答時間が長くなるため、非効率です。
    • 単一テーブルとし、シミュレーション毎に計算で出すようカラムを分けるよう変更します。

今後

  • 画面遷移、データモデルをもう少し詳細化します

参考

  • flask-security

  • セキュリティ

  • パフォーマンス

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0