※ 自分用メモのため、誤字脱字は後ほど修正します。
また、いくつか未完了部分は
 にしています。 ※
目的
- 下記記事で、AlmaLinux8.9の仮想サーバの新設とminikubeのインストールを行いましたが、ノートPC環境下(メモリ8GB、CPU4GB)において、オーバーヘッドが生じました。
 - 影響として、下記事象が発生しています。
- インターネット検索、ウイルススキャンといった動作が遅延する
 - ワーカノードの起動時間に10分ほどかかっている
 - リソース不足になると、kubectlコマンドのレスポンスが遅い
 
 - スペックの高いPCに変更しない限り解消ができないため、別なkubernetesのディストリビューションを入れることを検討しました
 - また、収支管理目的で構築したLAMPサーバ(Apache、Java、MySQL等)の新たなモダンアーキテクチャ構成も考えることとしました
- 【移行・設計】centos7からalmalinuxへの移行Tips
https://qiita.com/t_kyn/items/91f6dbb7c3ef938b9377 
 - 【移行・設計】centos7からalmalinuxへの移行Tips
 
内容
- minikubeよりも軽量なKubernetesディストリビューション(k3s、k3d)を候補としました。
 - 上記を踏まえて、k3dでの構築する方針としています。
 
構築の大まかな流れ
- minikubeをアンインストール
- minikubeをアンインストールします
 
 - k3dインストール
 - Kubernatesのマニフェストファイルに沿って、起動する
 - ミドルウェアをカスタマイズしていく(後述)
 
構築のイメージ図
- イメージ図は以下の通りです
 - 仮想サーバ1台をノートPC上に作成しています。
 - ディスク使用率を節約するため、コンテナイメージはalpineLinuxをベースとします
 
k3dとしてのアーキテクチャ
- 1つの仮想サーバに下記のコンテナ(Loadbalancer、MasterNode、WorkerNode1、WorkerNode2)が搭載されています。
 
k3dのアーキテクチャ(Pod含む)
- Deployment、Service、Pod、PersistenVolumeを含む構成としては下記の通りとなっております。
 - SharedDiskの箇所は、仮想マシンの共有ディレクトリをマウントしています。
 
Webサーバ
- 
Webサーバ:ApacheからNginxに変更
- 選定理由
- Nginxはアクセスを振り分ける負荷分散やApacheよりも処理性能が高いため
 
 - 用途・役割
- 外部アクセスをhttps通信で受付し、PythonPodへhttpリスエストするようプロキシします
 - nginxのPodからservice経由で、PythonのPodへ接続します。
- serviceの名前解決を利用し、nginxからPythonへの接続設定を行います
 
 - パスベースで転送先を設定します
 
 
 - 選定理由
 
APサーバ
- 
APサーバ:TomcatからPythonに変更
- 選定理由
- PCのオーバーヘッド防止
- javaからpythonにすることで、メモリ消費量の削減を期待したため
 - Metabaseの内部結合処理で、JavaVMで使用するメモリを多く消費するため
 
 - Pythonでの実装はGoogle Colaboratryでスマートフォンでの動作検証も手軽に実施できるため
 - 将来性の期待
- データ分析やAI、機械学習分野の知識習得も視野に入れたため
 
 - 参考
 
 - PCのオーバーヘッド防止
 - 用途・役割
- Flask、SQLAlchemy、Pandasといったシンプルながらも拡張性・実装がしやすいモジュールを利用します
 - 自作アプリケーションを作成することも視野に入れます
 
 - 工夫点
- 機能の細分化
- アプリケーションの機能がわかるよう、フォルダ、ファイルを分けます
 
 - アプリケーション間の紐づけ
- Web-APサーバ間:Flaskのblueprintを利用
 - AP-DBサーバ間:SQLAlchemyを利用
 
 - SQLalchemyを利用することで、pythonのソースコードにSQL文を実装しない設計にします
 - 操作の種類単位にクラスを作ります
 - テーブルとクラスを1対1で対応させ、Modelsを用います。
 - データフレームやピボット、プロットの作成が可能なpandas、plotlyを導入します
 
 - 機能の細分化
 
 - 選定理由
 
DBサーバ(deploymentとserviceを構築)
- 
DBサーバ:MySQLで現状維持(バージョンアップは行う)
- 選定理由
- 既存SQLの作りこみもあり、他のDBMSへの移行が難しくなるため、MySQLを採用します
 
 - 用途・役割
- データの保管・集計を行います
 
 - 工夫点
- デフォルトではソケット通信が採用され、3306ポートがListenせず、localhostのみアクセスができません
 - そこで、下記を変更することで、MySQLコンテナ上で3306ポートをListenしています
- MySQLのbind-address = 0.0.0.0に変更
 - MySQLのskip-network設定は除外
 - 3306ポート及びソケットファイルを定義
 
 - 更新・参照のServiceは別々とします(masterデータベースのみ書き込みできるようにするため)
- データベース読込用と書込用とでServicePortを分ける
- R/W用 Service Port:3306→TargetPort:3306
(Selectorでマスターデータベースのみに接続制限) - R/O用 Service Port:3307→TargetPort:3306
 
 - R/W用 Service Port:3306→TargetPort:3306
 
 - データベース読込用と書込用とでServicePortを分ける
 - データ更新
- 下記で扱ったダンプファイルをエクスポートし、MySQLマスターデータベースに対して、インポートしています。
- 【移行・設計】centos7からalmalinuxへの移行Tips
https://qiita.com/t_kyn/items/91f6dbb7c3ef938b9377 
 - 【移行・設計】centos7からalmalinuxへの移行Tips
 
 - 下記で扱ったダンプファイルをエクスポートし、MySQLマスターデータベースに対して、インポートしています。
 
 
 - 選定理由
 
DBサーバのストレージ
- PersistentVolume、PerisitentVolumeClaimを個々のMySQLpodに1つ割り当てます
 - MySQL-1、MySQL-2、MySQL-3に分け、ストレージもmysqlpv1、mysqlpv2、mysqlpv3と分けていきます
 
Pythonの実装方針
- Flask、Pandas、SQLAlchemy、plotlyをpip installし導入します。
 - 作成するアプリケーションは下記の通り
- 
flaskAPI
- 
 flaskをベースとしたアプリケーションにする - 
 ページの動的作成 
 - 
 - 
BIツール専用データベース管理
- 
 給与 - 
 ライフライン - 
 支出 - 
 収入 - 
 健康管理 - 
 株価 - 
 サーバ稼働情報 - 
 ログ保管 - 
 待機系データベース状態確認 
 - 
 - 
AP-DB間のマッピング
- 
 SQLはキーワードごとにまとめて、マッピングファイル化する - 
 マッピングはFlaskのBlueprintで実装 - イメージ図は下記の通り
 - 
MySQL1、MySQL2、MySQL3はpod。障害発生時、Pod(Kubernetes)の仕様により再起動し、既存のデータベースに再接続が可能
 
 - 
 - 
BIツール(フォーマット可変)- 
 Plotlyによるデータベース可視化ツール作成 - 
 グラフの拡大・縮小 
 - 
 - 
 wordpress風のドキュメント作成ツール(markdownか、他で検討中) - 
Web-AP間のマッピング
- 
 APIの実装(各URLより、適切なテーブル・グラフを表示) 
 - 
 - 
 外部から接続できるようにする- 
 ロードバランサーでhttpsで待ち受けて、httpで受付する。 - 
 証明書は以前から使っているSSL証明書を流用する - 
 外部接続をするために踏み台をもう一つ作る 
 - 
 
 - 
 
構築方法
構築方法の流れをまとめました。
レジストリを登録
k3dでカスタムイメージを作成する場合、
コンテナベースのレジストリを登録する必要があるので作成します。
- ディレクトリ作成
mkdir -pv /datamnt/data ~/.k3d_t_kyn - ボリューム作成
docker volume create local_registry docker volume ls | grep local_registrylocal - レジストリ用のコンテナ作成
docker container run -d --name tkynregistry.local -v local_registry:/datamnt/imgs --restart always -p 5000:5000 registry:2.8.3 
k3dクラスタ作成
k3dでクラスタ作成を行います。クラスタ作成時に下記も同時に作成します。
共有ボリュームと共有レジストリは、ワーカノードとホストマシンのディレクトリやファイルをマッピングします。
最上位のディレクトリを共有マウントとして利用します。
まとめると下記図表になります。
| 内容 | 構成 | 
|---|---|
| コントロールプレーン | 1台構成 | 
| ワーカノード | 2台構成 | 
| ロードバランサ | 1台構成 | 
| 共有ボリューム | /datamnt/data=/datamnt/data | 
| ローカルレジストリ | ~/.k3d_t_kyn/t_kyn_config.tmplate=/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl | 
上記の設定で、下記コマンドでクラスタ作成します。
- 
クラスタ作成
k3d cluster create --agents 2 --port "8080:30000@loadbalancer" --wait 0 --volume ~/.k3d_t_kyn/t_kyn_config.tmplate:/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl --volume /datamnt/data:/datamnt/data - 
コンテナレジストリを作成し、起動・ホストOS上で名前解決できるようにします。
これを行うことで、k3dのワーカノード上にカスタムイメージを利用したPodを、ワーカノード上に作成することが可能になります。# コンテナのネットワークを確認する+クラスタの名前を確認する docker network ls kubectl config get-contexts # クラスタをコンテナとネットワークを接続する docker network connect k3d-0 tkynregistry.local # コンテナレジストリへの起動確認 curl -kv http://tkynregistry.local:5000 # ホストへの登録をします。 cat /etc/hosts | grep tkynregistry.local 
コンテナのイメージ登録
- コンテナのイメージを登録します。
 
NGINXのPod、Deployment、Service作成
- 
Nginxのデフォルトコンテナイメージをpullします
docker pull nginx:alpine3.18 - 
Dockerfile及び、default.confをカスタマイズします
- 詳細はGithubの下記参照(プライベート用リポジトリ)
 
 - 
Dockerfileをもとにデプロイします
# Dockerfileをもとにデプロイ cd /home/kubeuser/podmultinode/imagenginx;docker build -t imagenginx:v1.0.0 .;cd ~ # ビルドしたイメージの登録(タグ付け) docker tag imagenginx:v1.0.0 tkynregistry.local:5000/nginx:v1.0.0 # ローカルレジストリへpush docker push tkynregistry.local:5000/nginx:v1.0.0 - 
deploymentのnginxpodを3つ、serviceを作成します
kubectl get pod kubectl apply -f /home/kubeuser/podmultinode/podnginx.yml kubectl get pod 
PythonのPod、Deployment、Service作成
- 
pythonのデフォルトコンテナイメージをpullします
docker pull python:alpine3.18 - 
Dockerfile及び、pythonファイルをカスタマイズします。
ls -lrt /home/kubeuser/podmultinode/imagepython/Dockerfile vi /home/kubeuser/podmultinode/imagepython/Dockerfile →5000番はregistryでつかうので、5520に変更。 →pip installするモジュールを選定し、必要なパッケージを入れてください。 - 
Dockerfileをもとにデプロイします。
# Dockerfileをもとにデプロイ cd /home/kubeuser/podmultinode/imagepython;docker build -t imagepython:v1.0.0 .;cd ~ # ビルドしたイメージの登録(タグ付け) docker tag imagepython:v1.0.0 tkynregistry.local:5000/imagepython:v1.0.0 # ローカルレジストリへpush docker push tkynregistry.local:5000/imagepython:v1.0.0 - 
コンテナの動作確認をします
docker images docker run -d --name testpythoncontaner imagepython:v1.0.0 docker exec -it testpythoncontaner hostname -i curl http://CONTENANOIP:5520 - 
pythonPod、Deploymentを起動します。
###事前にtestapp.pyを配置 cp -pv /home/kubeuser/podmultinode/imagepython/testapp.py /datamnt/data/pyscripts/ kubectl get pod kubectl apply -f /home/kubeuser/podmultinode/podpython.yml kubectl get pod - 
※テスト確認
kubectl describe service kubectl get pod,svc -o wide kubectl exec -it python-app-pod /bin/sh apk add curl hostname -i curl http://localhost:5520 
参考:https://docs.docker.jp/engine/reference/commandline/build.html
Nginx→Pythonへの疎通確認
- それぞれ下記コマンドで疎通確認可能です
# コンテナ内部から curl http://nginx:30000/t_kyn_py_test/ # コンテナホストから curl http://172.23.0.3:30000/t_kyn_py_test/ 
MySQLのPod、Deployment、Service作成
- MySQLで、デフォルトコンテナイメージがないため、alpine:3.18をpullします
docker pull alpine:3.18 - Dockerfile、entrypoint.sh、my.cnfをカスタマイズします。
(詳細は下記Github参考)- https://github.com/t-kyn-git/alma_t_kyn003_k3d_infrastructures-/blob/main/podmultinode/imagemysql/Dockerfile
 - https://github.com/t-kyn-git/alma_t_kyn003_k3d_infrastructures-/blob/main/podmultinode/imagemysql/entrypoint.sh
 - https://github.com/t-kyn-git/alma_t_kyn003_k3d_infrastructures-/blob/main/podmultinode/imagemysql/conf/my.cnf
 
 - Dockerfileをもとにデプロイします。
# Dockerfileをもとにデプロイ cd /home/kubeuser/podmultinode/imagemysql;docker build -t imagemysql:v1.0.0 .;cd ~ # ビルドしたイメージの登録(タグ付け) docker tag imagemysql:v1.0.0 tkynregistry.local:5000/imagemysql:v1.0.0 # ローカルレジストリへpush docker push tkynregistry.local:5000/imagemysql:v1.0.0 - MySQLPodをデプロイした際の工夫点
- DeploymentでMySQLPod3つを起動
 - StatefulSetが使えないので、DeploymentでPodを3つ作成
 - 
 データベースの冗長化について
- 仮想マシンのcronで定期的に下記を実行するスクリプトを開発中です
- Masterデータベースからデータベースエクスポート
 - Slaveデータベースへインポート
 
 
 - 仮想マシンのcronで定期的に下記を実行するスクリプトを開発中です
 - データベースの永続化
- PersistentVolumeとPersistentVolumeClaimも作成します。
 - StorageClassはSlowにします。
 
 
 - MySQLPodを起動します。
kubectl get pod kubectl apply -f /home/kubeuser/podmultinode/podmysql.yml kubectl get pod 
MySQLの性能を念のため確認
- テスト用のテーブルを作成します
 - データを1万行挿入するプロシージャを作成し、繰り返し実行できる関数を作成します。
 
mysql -u root -p mysql
create table unko(id text);
DELIMITER //
CREATE PROCEDURE loop_insert()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE max_count INT DEFAULT 10000;
WHILE i < max_count DO
INSERT INTO unko VALUES ('aaaaaaaaaa');
SET i = i + 1;
END WHILE;
END //
DELIMITER //
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < 1000 DO
CALL loop_insert();
SET i = i + 1;
END WHILE;
END //
CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert(); CALL loop_insert();//
- 結果としては、45万件の挿入は全く問題ありませんでした。
 
Loadbalancerについて
- ロードバランサはk3dクラスタ作成時に
「 --port "8080:30000@loadbalancer"」を指定して作成されるので、
8080ポートでワーカノードの30000ポートに接続できるようにしていますので
追加で行うことは特にしないです。 - SSHポートフォワーディングすれば、ブラウザからの接続が可能になります。
 
 ForwardProxy用サーバから接続する
- nginxPodにhttps→http(ノードIPに接続)接続できるよう設定追加します
 - 自己証明書(SSL証明書)を利用します。
- 旧LAMPサーバから利用しているSSL証明書を利用
 
 - 通信経路
- 一般ユーザ(User)は、許可されたネットワーク機器からのみ接続可能
 - AdminUserはhttpsで直接接続する
 - AdminUser(PC)ではポートフォワーディング設定後、接続(非推奨)
 
 
カスタムイメージの更新
- k3dではイメージがプッシュできないことがあります
- 削除APIでregistry上のイメージ削除が不可能。
 - コンテナRegistry上のイメージファイルを削除・再pushしてもワーカノード経反映されない(k3dのバグかと思います)
 - k3dクラスタを削除→再作成すると、正しいコンテナイメージでデプロイが可能です。
https://gist.github.com/takenoco82/b9559a1abd57eb0845a77041860cd26e
https://qiita.com/tororoMeshi/items/98eac9b202adaa266d61 
 - 下記の方法でクラスタ再作成をします。
kubectl delete -f /home/kubeuser/podmultinode/podnginx.yml kubectl delete -f /home/kubeuser/podmultinode/podpython.yml kubectl delete -f /home/kubeuser/podmultinode/podmysql.yml k3d stop cluster 0 k3d cluster delete k3d create(同じコマンド) カスタムイメージのtag付け及びpush kubectl apply -f /home/kubeuser/podmultinode/podmysql.yml kubectl apply -f /home/kubeuser/podmultinode/podpython.yml kubectl apply -f /home/kubeuser/podmultinode/podnginx.yml - system pruneはイメージファイル含めて削除する可能性が高いため非推奨です。
docker system prune -f 
k3dクラスタの再起動
- クラスタ再作成せず、継続利用する場合、下記コマンドでクラスタ再起動が可能です。
# k3dクラスタの停止 k3d cluster stop 0 # k3dクラスタの起動 k3d cluster start 0 
PythonからMySQLへの接続確認
- 事前に、pip install mysql.connector
を行った後、下記コマンドでデータベース検索を行います。 - つながらない場合はリスナーが起動していないと思われますので、下記対応
- MySQLの設定ファイルより、bind-addressの設定を0.0.0.0にする
 - Python側で設定しているMySQL接続パスワードが正しいか確認する
import mysql.connector # MySQLサーバーの接続情報を設定 config = { 'user': '★秘密★', 'password': '★秘密★', 'host': '★秘密★', 'database': '★秘密★', 'port': ★秘密★ # ポート番号を指定 } # MySQLサーバーに接続 try: conn = mysql.connector.connect(**config) if conn.is_connected(): print('MySQLに接続しました') # クエリを実行するためのカーソルを作成 cursor = conn.cursor() # クエリを実行 cursor.execute('SELECT count(*) FROM user_tables;') # 結果を取得 rows = cursor.fetchall() print('取得した行数:', len(rows)) for row in rows: print(row) except mysql.connector.Error as err: print('MySQLに接続でエラーが発生しました:', err) finally: # 接続をクローズ if 'conn' in locals() and conn.is_connected(): cursor.close() conn.close() print('MySQL接続をクローズしました') 
 
トラブルシューティング
k3dクラスタもコンテナ全削除を削除するとき
- k3d cluster delete 0
 - docker network disconnect k3d-0 t_kyn_registry_host
 - docker network rm k3d-0
 - それでも消えなかったら「docker system prune -f」する
 
k3dの様々なバグ
様々な不具合が出たので、検討のうえ対策を施しました。
一部は恒久的に難しい課題があるため、暫定対応で対処しています(
で記載)
1.k3dのコンテナイメージが正しく登録できない。削除もできない(課題)
- imageがRegistryに最新imageが登録されているのにもかかわらず、ワーカノード上で参照するイメージが最新化できない。
 - 暫定としては、k3dクラスタ再作成で今後も運用していきます
 - それでも解決しない場合、「docker system prune -f」で削除してから再登録
 
2.MySQLコンテナ起動時、CPUが高騰する
- イメージファイルでのentrypoint.shでncコマンドでwhileループをかけている。その処理は不要にして削除しています。
 - ENTRYPOINT ["/entrypoint.sh"]
CMD ["mysqld", "--user=mysql", "--datadir=/var/lib/mysql"]
から、
command: ["sh", "/entrypoint.sh"] とするように修正することで解決しています 
3.CPU Limits設定できない。
- k3dのバグだと思われる。解決はできてないです。
 - 負荷をかけないようにする以外に解決策がないため、pod、node、仮想マシンのCPU使用率を1分間隔で取得し、結果を解析するようにしたいと思います(実装中です)
 
4.k3dで使用できるカスタムイメージの削除APIが使えない。
- 下記はサポートされてなかったので使えない。解決不可。
 - 
暫定としては、「docker system prune -f」で削除
curl -X GET -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -s -D - http://tkynregistry.local:5000/v2/imagemysql/manifests/v1.0.0| grep Docker-Content-Digest: curl -X DELETE -D - http://localhost:5000/v2/imagemysql/manifests/sha256:3d1243e60f980f5510c3642c60b596aa9750b846c15ee2ca957614fc9b32d00c 
5.MySQLはデフォルトで外部接続設定は許可されてない。ポートもListenしない。
- 少なくともMariaDBは許可されてない。
 - https://helpcenter.gmocloud.com/altus/s/article/ch-4024
 - https://tutorialcrawler.com/database/mysql%E3%81%B8%E3%81%AE%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E6%8E%A5%E7%B6%9A%E3%82%92%E8%A8%B1%E5%8F%AF%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/
 - my.cnfのskip-networkの設定を無効にした。
 
6.MySQLのデータベースで、AccessDenyで接続できない
- Service名とPort指定で接続できた
 - MySQLのデータベースのPrivilageの設定を実施
 
 7.MySQLのPod起動時、ymlに指定するPersistenVolumeが適切なPodへマウントできない(例)MySQL1⇒PV3にマウントされる)
- Podの起動順序があっていない
 - 対処方法としては、下記2案で検討中ですが案1を採用し、スクリプト開発済みです。
- 
案1:◎マウント状態確認(kubectl describe pv,pvc)し、紐づいていなければpodのデプロイをやり直しするスクリプトを開発 - 
案2:△1⇒2⇒3の順に起動できるようにマニフェストファイルに待機時間を入れる 
 - 
 
Pythonでの実装検討(次回予告)
- 「Pythonの実装方針」に記載はしている点と重複しますが、次章で下記内容を記載します
 - どの画面からどの画面(URL)に遷移するのかの図も整理していこうと思います
- 
FlaskAPI
- 
データ表示、データモデルの定義 - 
クライアントからのリクエストを適切なテーブルへ割り振り - 
アプリケーションの認証機能 
 - 
 - 
SQLAlchemy
- 
SQL実行、データマッピング 
 - 
 - 
Pandas
- 
データフレーム・レイアウトの定義 
 - 
 - 
Plotly
- 
グラフ作成 
 - 
 
 - 
FlaskAPI
 
参考
- 
k3dでPrivateRepositoryを使う
https://qiita.com/yuta_vamdemic/items/5ee72f8182310272360c - 
k3dとLocal Registry
https://qiita.com/oruharo/items/6aea30ae69c0fb8009ed - 
【Docker】MySQLで複数database環境を構築する
https://qiita.com/piggydev/items/2096258dc45a6bbe2a19 - 
【LearnTechnoBios /rest-api-mysql-k3d
https://github.com/LearnTechnoBios/rest-api-mysql-k3d/blob/main/Dockerfile