※ 自分用メモのため、誤字脱字は後ほど修正します。
また、いくつか未完了部分は にしています。 ※
目的
- 2024年4月で、centos7からalmalinuxへの移行において、docker+minikubeを入れる方針で、minikubeインストールまでを行いました。
- メモリ8GB、CPU4GBのノートPC環境下において、minikubeを利用しています。
- しかしながら、1つのワーカノードを起動する度にデフォルトで2200MBのメモリを使用するため、ノートPCがリソース不足となることが判明し、下記のような影響が生じました。
- インターネット検索、ウイルススキャンといった動作が遅延する
- ワーカノードの起動時間に10分ほどかかっている
- リソース不足になると、kubectlコマンドのレスポンスが遅い
- 上記解消にはよりスペックの高いPCに変更しない限り難しいため、別なkubernetesのディストリビューションを入れることを検討しました
- また、同時に新たなモダンアーキテクチャ構成を考えることとしました
内容
- 前提として、minikubeよりも軽量のディストリビューションを検討した結果、軽量な2つのKubernetesディストリビューション(k3s、k3d)を候補としました。
- k3sの場合、ワーカーノードを同一の仮想マシンに構築できないという制約がある
- k3dでは、同一の仮想マシンにk3sのワーカノードとしてdockerコンテナを作成し、配下にPodを仮想的に作成することができます。反面、StatefulSetが公式サイトへ記載されてない面もあるため、データを永続化したり、レプリカデータベースを作成し冗長化したい場合は代替構成の検討が必要
- 上記を踏まえて、k3dでの構築で舵を切りました
構築の大まかな流れ
- minikubeをアンインストール
- 既存のminikubeをアンインストールします
- k3dインストール
- Kubernatesのマニフェストファイルに沿って、起動する
- ミドルウェアをカスタマイズしていく(後述)
構築のイメージ図
-
イメージ図は以下の通りです
-
以下の構成が含まれる仮想サーバをノートPC上に作成しています
-
ディスク軽量化するため、コンテナイメージはalpineLinuxをベースとします
Webサーバ
-
Webサーバ:ApacheからNginxに変更
- 選定理由
- Nginxはアクセスを振り分ける負荷分散やApacheよりも処理性能が高いため
- 用途・役割
-
外部からhttpsで受付し、PythonPodへhttpリスエストするようプロキシします
-
nginxのPodからservice経由で、PythonのPodへ接続します。
- serviceの名前解決を利用し、nginxからPythonへの接続設定を行います
-
パスベースで転送先を設定します
-
- 選定理由
APサーバ
-
APサーバ:TomcatからPythonに変更
- 選定理由
- Javaの実行環境はノートPC上でしか作業ができないため不便なのに対し、
Pythonでの実装はGoogle Colaboratryでスマートフォンでの動作検証も手軽にできるため。 - Metabaseで内部結合処理のSQLで、多くのJVMを消費する事象が発生しました
- javaからpythonにすることで、メモリ消費量の削減を期待しました
- 将来伸びる分野であろうデータ分析やAI、機械学習といった分野に対しての勉強も視野に入れます
- https://engineer-style.jp/articles/8766#JavaとPythonのデメリットとは
- Javaの実行環境はノートPC上でしか作業ができないため不便なのに対し、
- 用途・役割
- Flask、SQLAlchemy、Flask-Securityといったシンプルながらも拡張性・実装がしやすいモジュールを利用します
- 自作アプリケーションを作成することも視野に入れます
- 工夫点
- アプリケーションの機能がわかるよう、フォルダ、ファイルを分けます
- 紐付けするため、flaskのblueprintを利用しました
- SQLalchemyを利用することで、SQL文を実装しない設計にします
- 操作の種類単位にクラスを作ります
- テーブルとクラスを1対1で対応させます
- データフレームやピボット、プロットの作成が可能なpandasを導入します
- グラフの保存はmatplotlibが必要なので必要であればインストールして使います
- https://note.com/tobeblogger/n/n2250f1338922
- 選定理由
DBサーバ(deploymentとserviceを構築)
-
DBサーバ:MySQLで現状維持(バージョンアップは行う)
- 選定理由
- 既存SQLの作りこみもあり、他のDBMSへの移行が難しくなるため、MySQLを採用します
- 用途・役割
- データの保管・集計を行います
- 工夫点
- デフォルトではソケット通信が採用され、3306ポートがListenせず、localhostのみアクセスができません
- そこで、下記を変更することで、MySQLコンテナ上で3306ポートをListenしています
-
MySQLのbind-address = 0.0.0.0に変更
-
MySQLのskip-network設定は除外⭐★
-
-
更新・参照のServiceは別々とします(masterデータベースのみ書き込みできるようにするため)
- 3306はR/W用
- 3307はR/O用
-
データ更新
- 選定理由
DBサーバのストレージ
-
PersistentVolume、PerisitentVolumeClaimを個々のMySQLpodに1つ割り当てます
-
MySQL-1、MySQL-2、MySQL-3に分け、ストレージもmysqlpv1、mysqlpv2、mysqlpv3と分けていきます
Pythonの実装方針
-
Flask、Pandas、SQLAlchemy、Flask-security、Matplotlibをpip installし導入します。
-
作成するアプリケーションは下記の通り
-
flaskAPI
-
flaskをベースとしたアプリケーションにする
-
ページの動的作成
-
-
BIツール専用データベース管理
-
給与
-
ライフライン
-
支出
-
収入
-
健康管理
-
株価
-
サーバ稼働情報
-
ログ保管
-
待機系データベース状態確認
-
-
SQLファイルキーマッピング
-
SQLはキーワードごとにまとめて、マッピングファイル化する
- マッピングは「key,モード,SQLalchemyのメソッド名,タイムアウト値、任意パラメータ1~10まで」とする
→キーワードだと複雑化したため、modelとrouteを分離して作成 - タイムアウト値を超えたら強制終了する
- モードは、以下モードに応じて、挙動変更を行う
- モード:summary、
- summary:結果OK、NGのみを返却
- detail:詳細な結果を表示
- イメージ図は下記の通り
- MySQL1、MySQL2、MySQL3はpod。PodはKubernetesの仕様により、障害発生時再起動され、既存のデータベースに再接続する
-
-
BIツール(フォーマット可変)
* -
wordpress風のドキュメント作成ツール(markdownか、他で検討中)
-
APIのパステスト用実装
-
-
グラフつくる
-
web表示できるようにする
-
外部から接続できるようにする
-
ロードバランサーでhttpsで待ち受けて、httpで受付する。
-
証明書は以前から使っているSSL証明書を流用する
-
外部接続をするために踏み台をもう一つ作る
-
最低スペックの仮想サーバを使い、squidでプロキシする
-
構築方法
構築方法の流れをまとめました。
レジストリを登録
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の場合
-
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の場合
-
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を起動します。
###事前に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の場合
- 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
- ポイントが数点
- DeploymentでMySQLPod3つを起動しています。
- StatefulSetが使えないので、DeploymentでPodを3つ作成しています。
-
データベースの冗長化については、定期的にデータベースを入れ替えるか、テーブルインポートで別途検討中です。
- データベースを永続化するため、PersistentVolumeとPersistentVolumeClaimも作成します。StorageClassはSlowにします。
- MySQLPodを起動します。
kubectl get pod kubectl apply -f /home/kubeuser/podmultinode/podmysql.yml kubectl get pod
MySQLの性能を念のため確認
- 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用サーバから接続する
- https→http(ノードIPに接続)接続するサーバを外部に建てる
- 自己署名書(SSL署名書)
- LAMPサーバからずっと使用している大谷翔名書を利用する。
- 通信経路
- 外部通信は内部の仮想マシン(k3d Internal)のDockerBuildでImageのダウンロードに失敗することがあるため、
- 外部通信は内部の仮想マシン(k3d Internal)のDockerBuildでImageのダウンロードに失敗することがあるため、
カスタムイメージの更新
- k3dではイメージがプッシュできない事象があるようです。(バグかと思います)
- registryのAPIを実行するやり方では削除は不可でした。
- そのため、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がpullされたままになる
- 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のバグだと思われる。解決はできてないです。
- 負荷をかけないようにするしかない
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
- データフレーム・レイアウトの定義
-
Matplotlib
- グラフ作成
-
Flask-security
- アプリケーションの認証機能
-
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