ローカルMQTTブローカーの開発

GMKTech NucBox5(中古)
hostnamectl
Operating System: Ubuntu 24.04.2 LTS
Kernel: Linux 6.8.0-53-generic
Architecture: x86-64
Hardware Vendor: GMKtec
Hardware Model: NucBox5
Firmware Version: KB05_GMKTEC_1_02_AUTO001
Firmware Date: Tue 2022-07-26

echo $XDG_CURRENT_DESKTOP
LXQt

dpkg -l | grep lubuntu-desktop
ii lubuntu-desktop 24.04.10 amd64 Lubuntu Desktop environment

システム安定化設定
- コア 0が現在、仕事が少ないみたい→ コア0をMQTTブローカーの設定する。
- Intel P-Stateを無効化する
- performanceガバナーを設定する。
- CPUクロックが最大クロックで固定されているかを確認する。
Intel P-State無効化し、CPUをperformanceへ設定、周波数固定化
sudo nano /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_pstate=disable cpufreq.default_governor=performance processor.max_cstate=0 idle=poll hwp_off=1"
sudo update-grub
sudo reboot
確認
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
performance
C-Stateが無効化されているか?
sudo cat /sys/module/processor/parameters/max_cstate
0
HWPが無効化されているか?
ring@ring-nucbox5:~$ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_driver
acpi-cpufreq
周波数が安定しているか?
watch -n 1 "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq"
周波数の値が安定しているか?
Ctrl +cで終了

負荷テストの実施
stress -c 4 -t 600
したら周波数が2.8GHzから2.6GHzまで落ちた。
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
最大周波数を落として、安定化させる。
対応周波数の確認
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
2000000にする。
sudo nano /etc/systemd/system/cpu-freq-limit.service
[Unit]
Description=Set CPU Max Frequency to 2.0GHz for Stability
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do echo 2000000 > $i; done'
ExecStart=/bin/bash -c 'for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance > $i; done'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable cpu-freq-limit.service
sudo systemctl start cpu-freq-limit.service
確認
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq
2000000
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
performance

検査
温度
watch -n 1 "sensors"
負荷
stress -c 4 -t 600
クロック
watch -n 1 "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq"

負荷試験とデータ収集
nano cpu_logging.sh
600 → 10分ごとに計測
#!/bin/bash
while true; do
echo "$(date +%s),$(sensors | grep 'Tctl' | awk '{print $2}' | tr -d '+°C'),$(cat /sys/class/powercap/intel-rapl:0/energy_uj),$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq)" >> cpu_data.csv
sleep 600
done
権限付与
chmod +x cpu_logging.sh
実行
./cpu_logging.sh & LOG_PID=$! && stress -c 4 -t 86400 && kill $LOG_PID
電源確認
cat /sys/power/state
2025/02/15 12:53 電源設定確認中&負荷試験実行はまだ。

電源管理:スリープ無効化
- systemd-logind
- xfc4-power-manager
systemd-logind
現在の設定の確認
cat /etc/systemd/logind.conf | grep -E 'HandleLidSwitch|IdleAction'
設定の修正
sudo nano /etc/systemd/logind.conf
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
IdleAction=ignore
IdleActionSec=0
設定を適用
sudo systemctl restart systemd-logind
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
sudo reboot
設定されたか確認
systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target
全部maskされていること。

LXQtのスリープ無効化
mkdir -p ~/.config/autostart
cp /etc/xdg/autostart/lxqt-powermanagement.desktop ~/.config/autostart/
echo 'Hidden=true' >> ~/.config/autostart/lxqt-powermanagement.desktop
cat /etc/systemd/logind.conf | grep HandlePowerKey
sudo nano /etc/systemd/logind.conf
HandlePowerKey=poweroff

仮テスト
10分
sudo ./cpu_logging.sh & LOG_PID=$! && stress -c 4 -t 600 && kill $LOG_PID
20:52
データの確認
less cpu_data.csv

cpu_logging.py
sudo python3 cpu_logging.py
ctrl+Cで停止するまで、データを収集して保存する。
stressを実行し、温度やCPUクロック周波数などを取得する。
import time
import csv
import os
import psutil
import subprocess
# 設定値
SLEEP_INTERVAL = 60 # データ取得間隔(秒単位、10分)
CSV_FILE = "cpu_data.csv" # 出力CSVファイル名
ENERGY_UJ_PATH = "/sys/class/powercap/intel-rapl:0/energy_uj" # 消費エネルギーファイル
CPU_FREQ_PATH = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" # CPUクロック周波数ファイル
SENSORS_KEYWORD = "Package id 0" # `sensors` コマンドで取得する温度のキー
STRESS_DURATION = 600 # 負荷テストの実行時間(秒)
STRESS_CORES = 4 # 使用するCPUコア数
def get_cpu_temp():
"""CPUの温度を取得する"""
try:
result = subprocess.run(["sensors"], capture_output=True, text=True, check=True)
for line in result.stdout.split("\n"):
if SENSORS_KEYWORD in line:
parts = line.split()
if len(parts) >= 4: # インデックスエラーを防ぐ
try:
return float(parts[3].replace("+", "").replace("°C", ""))
except ValueError:
return -1.0 # 異常値として -1.0 を返す
except subprocess.CalledProcessError:
return -1.0 # `sensors` コマンドの実行失敗時
return -1.0 # `SENSORS_KEYWORD` が見つからなかった場合
def get_cpu_freq():
"""CPUのクロック周波数を取得する"""
with open(CPU_FREQ_PATH, "r") as f:
return int(f.read().strip()) / 1000 # kHz → MHz
def get_cpu_usage():
"""CPU使用率を取得する"""
return psutil.cpu_percent(interval=1)
def get_energy_uj():
"""消費エネルギー(microjoules)を取得する"""
try:
result = subprocess.run(["sudo", "cat", ENERGY_UJ_PATH], capture_output=True, text=True)
return int(result.stdout.strip())
except Exception:
return None
def run_stress():
"""負荷テストを実行する"""
print(f"Starting stress test: {STRESS_CORES} cores for {STRESS_DURATION} seconds")
subprocess.Popen(["stress", "-c", str(STRESS_CORES), "-t", str(STRESS_DURATION)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def main():
"""メイン処理:データを定期的に記録し、負荷テストを実行"""
prev_energy = get_energy_uj()
prev_time = time.time()
with open(CSV_FILE, "a", newline="") as csvfile:
writer = csv.writer(csvfile)
writer.writerow(["timestamp", "cpu_temp", "power", "cpu_freq", "cpu_usage"])
while True:
run_stress() # 負荷テストを実行
time.sleep(SLEEP_INTERVAL) # 設定した間隔でデータ取得
curr_energy = get_energy_uj()
curr_time = time.time()
energy_diff = (curr_energy - prev_energy) / 1_000_000 # µJ → J
time_diff = curr_time - prev_time
power = round(energy_diff / time_diff, 2)
cpu_temp = get_cpu_temp()
cpu_freq = get_cpu_freq()
cpu_usage = get_cpu_usage()
writer.writerow([int(curr_time), cpu_temp, power, cpu_freq, cpu_usage])
csvfile.flush()
prev_energy = curr_energy
prev_time = curr_time
if __name__ == "__main__":
main()