完善开源发布配置和使用文档

This commit is contained in:
2026-05-06 09:23:26 +08:00
parent 51578fab40
commit edc7d970a8
14 changed files with 296 additions and 218 deletions
+14
View File
@@ -0,0 +1,14 @@
.git
.github
.env
.env.*
!.env.example
.idea
.vscode
__pycache__
*.py[cod]
*.pyo
*.pyd
.DS_Store
README.md
CHANGES.md
+3
View File
@@ -0,0 +1,3 @@
HOST=192.168.1.100
USERNAME=root
PASSWORD=your_idrac_password
+44 -44
View File
@@ -1,58 +1,58 @@
name: Build and Push Docker Image name: Docker Image
on: on:
push: push:
branches: [ master ] branches:
- master
tags:
- "v*"
pull_request: pull_request:
branches: [ master ] branches:
- master
jobs: jobs:
build: docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Login to DockerHub - name: Login to Docker Hub
uses: docker/login-action@v2 if: startsWith(github.ref, 'refs/tags/v')
with: uses: docker/login-action@v3
username: ${{ secrets.DOCKER_USER }} with:
password: ${{ secrets.DOCKER_PASS }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Login to Harbor Registry - name: Extract Docker metadata
uses: docker/login-action@v2 id: meta
with: uses: docker/metadata-action@v5
registry: harbor.ay.lc with:
username: ${{ secrets.HARBOR_USER }} images: lkddi/dell-fans-controller
password: ${{ secrets.HARBOR_PASS }} tags: |
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Extract metadata (tags, labels) for Docker - name: Build Docker image
id: meta uses: docker/build-push-action@v7
uses: docker/metadata-action@v4 with:
with: context: .
images: | platforms: linux/amd64,linux/arm64
lkddi/dell-fans-controller-docker push: ${{ startsWith(github.ref, 'refs/tags/v') }}
harbor.ay.lc/library/dell-fans-controller tags: ${{ steps.meta.outputs.tags }}
tags: | labels: ${{ steps.meta.outputs.labels }}
type=ref,event=branch cache-from: type=gha
type=ref,event=pr cache-to: type=gha,mode=max
type=sha,prefix={{branch}}-
latest
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+8 -1
View File
@@ -1 +1,8 @@
.idea .env
.idea
.vscode
__pycache__/
*.py[cod]
*.pyo
*.pyd
.DS_Store
+14 -18
View File
@@ -1,21 +1,17 @@
# 修复日志 # Changelog / 更新日志
## 问题1:温度读取不准确 ## Unreleased
- **问题**:之前的代码无法正确解析IPMI传感器输出中的温度值
- **解决方案**
- 修改了`sensor()`方法,使用`ipmitool sdr`命令获取更准确的传感器数据
- 更新了`temperature()`方法,使用正则表达式正确提取温度值
- **结果**:现在能够准确读取所有温度传感器数据
## 问题2:风扇转速读取不准确 - 准备公开开源发布流程,Docker Hub 镜像统一为 `lkddi/dell-fans-controller`
- **问题**:IPMI原始命令无法返回设置的风扇占空比值 - GitHub Actions 改为 PR/master 构建验证,`v*` tag 才发布 Docker 镜像。
- **解决方案** - 新增 Docker、Docker Compose 和本机 Python 三种运行说明。
- 通过校准实验确定了RPM与百分比的转换关系:20%设置对应4800 RPM - 移除代码中的默认 iDRAC 地址、账号和密码,改为必须通过环境变量配置。
- 实现了基于RPM的百分比估算算法 - 增加 `.env.example``.dockerignore` 和 MIT License。
- 添加了适当的四舍五入逻辑以匹配典型的5%步进 - 清理 Python 缓存文件,避免将运行产物提交到仓库。
- **结果**:现在能够准确估算当前风扇转速百分比
## 技术细节 ## Previous Improvements
- Dell服务器的IPMI系统在手动风扇模式下,可通过`ipmitool sdr`命令获取准确的RPM值
- 风扇转速百分比通过公式计算:`(current_rpm / theoretical_max_rpm) * 100` - 使用 `ipmitool sdr` 读取温度和风扇传感器数据。
- 理论最大RPM基于校准数据:`4800 RPM * (100/20) = 24000 RPM` - 根据最高温度自动选择风扇转速,高温时交还 iDRAC 自动模式。
- 对 IPMI 会话失败和命令超时增加重试和日志。
- 使用 RPM 估算风扇百分比,降低部分 Dell 机型 raw 命令返回 0 时的影响。
+3 -4
View File
@@ -1,5 +1,5 @@
FROM ubuntu:22.04 FROM ubuntu:22.04
LABEL maintainer="joestar817@foxmail.com" LABEL maintainer="lkddi"
# 安装依赖并设置时区 # 安装依赖并设置时区
RUN apt update && apt install -y \ RUN apt update && apt install -y \
@@ -11,8 +11,8 @@ RUN apt update && apt install -y \
&& echo 'Asia/Shanghai' > /etc/timezone && echo 'Asia/Shanghai' > /etc/timezone
# 复制应用文件 # 复制应用文件
COPY . /dell-fans-controller-docker COPY . /dell-fans-controller
WORKDIR /dell-fans-controller-docker WORKDIR /dell-fans-controller
# 如果有requirements.txt则安装Python依赖 # 如果有requirements.txt则安装Python依赖
# COPY requirements.txt . # COPY requirements.txt .
@@ -23,4 +23,3 @@ WORKDIR /dell-fans-controller-docker
# 设置启动命令 # 设置启动命令
CMD ["python3", "start.py"] CMD ["python3", "start.py"]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 lkddi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -2
View File
@@ -5,5 +5,4 @@ help: ## show help message
build: ## build docker image build: ## build docker image
docker build -t dell-fans-controller-docker . docker build -t lkddi/dell-fans-controller:test .
+150 -127
View File
@@ -1,153 +1,176 @@
# Dell风扇控制器 # Dell Fans Controller / Dell 风扇控制器
## 项目简介 Dockerized fan controller for Dell iDRAC/IPMI servers. It reads temperature sensors through `ipmitool` and adjusts fan speed automatically to reduce noise while keeping the server safe.
Dell风扇控制器是一个自动化工具,通过IPMI接口监控Dell服务器温度并自动调节风扇转速。本项目基于原项目 joestar817/dell-fans-controller-docker 进行了大量改进和功能增强 通过 iDRAC/IPMI 自动读取 Dell 服务器温度,并根据温度调整风扇转速。适合希望降低噪音、但仍保留高温自动保护的家庭实验室、机房和 NAS 场景
### 主要特性 > Warning: manual fan control can cause overheating if configured incorrectly. Test with care and keep iDRAC automatic mode available as fallback.
>
> 警告:手动风扇控制配置不当可能导致过热。请先确认服务器散热状态,并保留 iDRAC 自动模式作为兜底。
- **精准温度监控**:通过IPMI接口获取服务器进出口、CPU等关键温度数据 ## Features / 功能
- **智能转速控制**:根据温度自动调节风扇转速,平衡散热效果和噪音
- **网络容错能力**:具备强大的网络连接容错机制,能处理网络波动
- **多架构支持**:支持 AMD64 和 ARM64 架构,兼容多种平台
- **自动构建部署**:通过 GitHub Actions 自动构建和推送 Docker 镜像
## 使用方法 - Reads Dell iDRAC sensor data with IPMI LANPlus.
- Automatically switches between manual fan speed and iDRAC automatic mode.
- Reuses sensor data within one control cycle to reduce iDRAC session pressure.
- Retries transient IPMI session failures and backs off after controller errors.
- Supports Docker multi-arch images: `linux/amd64` and `linux/arm64`.
### 1. 准备工作 - 通过 IPMI LANPlus 读取 Dell iDRAC 传感器数据。
- 根据最高温度自动设置风扇转速,超过阈值时交还 iDRAC 自动控制。
- 单次控制周期复用传感器数据,减少 iDRAC 会话压力。
- 对临时 IPMI 会话失败进行重试,并在控制器异常后等待下一轮。
- Docker 镜像支持 `linux/amd64``linux/arm64`
在开始使用之前,请确保: ## Quick Start / 快速开始
1. 登录iDRAC管理界面并启用IPMI服务 Before running, enable IPMI over LAN in iDRAC and make sure the host running this container can reach the iDRAC management IP.
2. 确保网络能够访问iDRAC管理接口
3. 准备好iDRAC的用户名和密码
### 2. Docker运行 运行前请先在 iDRAC 中启用 IPMI over LAN,并确认运行容器的主机可以访问 iDRAC 管理 IP。
### Docker
```bash ```bash
# 基本运行命令 docker run -d --name dell-fans-controller \
docker run -d --name=dell-fans-controller \
-e HOST=YOUR_IDRAC_IP \
-e USERNAME=YOUR_USERNAME \
-e PASSWORD=YOUR_PASSWORD \
--restart always \ --restart always \
lkddi/dell-fans-controller:latest
```
### 3. 配置参数
| 环境变量 | 说明 | 默认值 |
|---------|------|--------|
| HOST | iDRAC管理接口IP地址 | 192.168.1.100 |
| USERNAME | iDRAC用户名 | root |
| PASSWORD | iDRAC密码 | your_idrac_password |
## 工作原理
### 温度控制策略
系统会监控服务器的多个温度传感器,取最高温度值作为控制依据:
| 温度范围(℃) | 风扇转速(%) | 说明 |
|------------|------------|------|
| 0-50 | 15 | 静音模式 |
| 50-55 | 20 | 低速运行 |
| 55-60 | 30 | 中速运行 |
| 60-65 | 40 | 高速运行 |
| >65 | 自动模式 | 由iDRAC自动调节 |
### 智能控制机制
- **模式切换**:系统在手动模式和自动模式间智能切换
- **转速监测**:实时监测当前风扇转速,避免不必要的调整
- **状态跟踪**:记录和跟踪风扇模式和转速设置历史
## Docker镜像
### 支持的架构
- AMD64 (x86_64)
- ARM64 (aarch64)
### 镜像仓库
- Docker Hub: `lkddi/dell-fans-controller:latest`
### 自动构建
项目通过 GitHub Actions 实现了自动构建和部署:
- 每次推送代码到master分支时自动构建新镜像
- 支持多架构镜像构建
- 自动推送至Docker Hub和Harbor仓库
## 技术实现
### 核心功能
1. **精确的温度读取**:使用正则表达式解析IPMI传感器数据,准确提取温度值
2. **RPM到百分比转换**:通过校准数据建立RPM与风扇转速百分比的准确转换关系(20% = 4800 RPM
3. **网络容错机制**
- 5次重试机制
- 60秒超时设置
- IPMI会话建立失败的特殊处理
- 10秒重试间隔
### 配置选项
- 运行间隔:每60秒检查一次温度并调整风扇转速
- 网络超时:60秒超时限制
- 重试机制:5次失败后停止
## 部署示例
### Proxmox VE (PVE)
```bash
docker run -d --name=dell-fans-controller \
-e HOST=192.168.1.100 \ -e HOST=192.168.1.100 \
-e USERNAME=root \ -e USERNAME=root \
-e PASSWORD=calvin \ -e PASSWORD=your_idrac_password \
--restart always \
lkddi/dell-fans-controller:latest lkddi/dell-fans-controller:latest
``` ```
### 群晖NAS View logs / 查看日志:
在群晖的Docker应用中:
1. 搜索并下载 `lkddi/dell-fans-controller` 镜像
2. 创建容器并设置环境变量
3. 启用自动重启选项
## 安全说明
- 项目使用IPMI协议与服务器通信,需要相应的管理权限
- 建议使用专用的IPMI管理账户
- 定期更新镜像以获得安全补丁
## 故障排除
### 常见问题
1. **连接失败**:检查网络是否能访问iDRAC接口
2. **认证失败**:确认用户名和密码正确
3. **权限不足**:确保账户有IPMI控制权限
### 日志查看
```bash ```bash
docker logs dell-fans-controller docker logs -f dell-fans-controller
``` ```
## 免责声明 ### Docker Compose
手动调整服务器风扇转速可能带来过热风险,使用本项目前请充分了解风险并做好数据备份。对于使用本项目引发的任何问题,作者概不负责。 Copy the example environment file and edit the iDRAC settings:
## 贡献 复制示例环境变量文件,并修改 iDRAC 配置:
欢迎提交 Issue 和 Pull Request 来改进项目。 ```bash
cp .env.example .env
```
## 许可证 `.env` example / 示例:
该项目遵循原项目的开源协议。 ```env
HOST=192.168.1.100
USERNAME=root
PASSWORD=your_idrac_password
```
Start the service / 启动服务:
```bash
docker compose up -d
```
### Run with Python / 直接使用 Python 运行
Install dependencies / 安装依赖:
```bash
# Debian / Ubuntu
sudo apt update
sudo apt install -y ipmitool python3
# macOS with Homebrew
brew install ipmitool python
```
Run / 运行:
```bash
export HOST=192.168.1.100
export USERNAME=root
export PASSWORD=your_idrac_password
python3 start.py
```
## Configuration / 配置
| Variable | Required | Description |
| --- | --- | --- |
| `HOST` | Yes | iDRAC management IP address. / iDRAC 管理 IP。 |
| `USERNAME` | Yes | iDRAC username with IPMI permission. / 有 IPMI 权限的 iDRAC 用户名。 |
| `PASSWORD` | Yes | iDRAC password. / iDRAC 密码。 |
The application does not include default credentials. Missing variables will stop startup with a clear error.
程序不内置默认地址、账号或密码。缺少环境变量时会直接停止并输出明确错误。
## Temperature Policy / 温控策略
The controller reads all temperature sensors and uses the highest value.
控制器读取所有温度传感器,并使用最高温度作为控制依据。
| Temperature / 温度 | Fan Speed / 风扇转速 |
| --- | --- |
| `0-50 C` | `20%` |
| `50-55 C` | `25%` |
| `55-60 C` | `30%` |
| `60-65 C` | `40%` |
| `>65 C` | iDRAC automatic mode / iDRAC 自动模式 |
## Troubleshooting / 故障排查
Test IPMI manually first / 先手动测试 IPMI
```bash
ipmitool -H 192.168.1.100 -I lanplus -U root -P your_idrac_password mc info
ipmitool -H 192.168.1.100 -I lanplus -U root -P your_idrac_password sdr
```
Common issues / 常见问题:
- `Unable to establish IPMI v2 / RMCP+ session`: iDRAC IPMI service may be busy or unstable. Check network latency, duplicate monitoring scripts, and consider resetting iDRAC with `mc reset cold`.
- Connection failed / 连接失败:确认容器主机能访问 iDRAC 管理 IP。
- Authentication failed / 认证失败:确认用户名、密码和 IPMI 权限。
- Permission denied / 权限不足:建议使用专用 iDRAC 用户,并授予 IPMI 控制权限。
## Release / 发布新版本
Docker images are published only from Git tags matching `v*`. Pull requests and `master` pushes only build and verify the image.
Docker 镜像只在推送 `v*` tag 时发布。PR 和 `master` 推送只做构建验证,不覆盖 `latest`
```bash
git tag v1.0.0
git push origin v1.0.0
```
The tag workflow publishes:
- `lkddi/dell-fans-controller:latest`
- `lkddi/dell-fans-controller:v1.0.0`
- `lkddi/dell-fans-controller:1.0.0`
- `lkddi/dell-fans-controller:1.0`
- `lkddi/dell-fans-controller:1`
GitHub Actions requires these repository secrets:
- `DOCKER_USER`: Docker Hub username.
- `DOCKER_PASS`: Docker Hub access token. Do not use your account password.
## Development / 开发
Syntax check without generating tracked cache files:
```bash
python3 -c "import ast, pathlib; [ast.parse(pathlib.Path(p).read_text(), filename=p) for p in ['start.py','controller/client.py','controller/ipmi.py']]"
```
Build locally:
```bash
docker build -t lkddi/dell-fans-controller:test .
```
## License / 许可证
MIT
+6
View File
@@ -3,8 +3,10 @@ from controller.logger import logger
from controller.ipmi import IpmiTool from controller.ipmi import IpmiTool
# 风扇控制器:根据iDRAC温度传感器结果自动切换风扇模式和转速
class FanController: class FanController:
# 初始化控制器并记录iDRAC连接信息
def __init__(self, host: str, username: str, password: str): def __init__(self, host: str, username: str, password: str):
self.host = host self.host = host
self.username = username self.username = username
@@ -14,10 +16,12 @@ class FanController:
self.last_set_speed = None # 记录最后设置的风扇速度 self.last_set_speed = None # 记录最后设置的风扇速度
self.is_auto_mode = False # 记录当前是否为自动模式 self.is_auto_mode = False # 记录当前是否为自动模式
# 设置手动风扇速度
def set_fan_speed(self, speed: int): def set_fan_speed(self, speed: int):
logger.info(f'设置风扇速度: {speed}%') logger.info(f'设置风扇速度: {speed}%')
self.ipmi.set_fan_speed(speed) self.ipmi.set_fan_speed(speed)
# 根据最高温度计算目标风扇转速
def get_required_fan_speed(self, temperature: int) -> int: def get_required_fan_speed(self, temperature: int) -> int:
""" """
根据温度确定所需的风扇转速 根据温度确定所需的风扇转速
@@ -35,7 +39,9 @@ class FanController:
else: else:
return -1 # 表示应切换到自动模式 return -1 # 表示应切换到自动模式
# 执行一次完整的温度读取和风扇控制周期
def run(self): def run(self):
# 同一轮控制周期复用一次SDR结果,减少iDRAC会话压力
sensor_data = self.ipmi.sensor() sensor_data = self.ipmi.sensor()
temperature: int = max(self.ipmi.temperature(sensor_data)) temperature: int = max(self.ipmi.temperature(sensor_data))
logger.info(f'当前最高温度: {temperature}') logger.info(f'当前最高温度: {temperature}')
+18 -11
View File
@@ -3,7 +3,10 @@ import time
import re import re
from controller.logger import logger from controller.logger import logger
# IPMI命令封装器:负责调用ipmitool读取传感器并设置Dell风扇
class IpmiTool: class IpmiTool:
# 初始化iDRAC连接参数
def __init__(self, host: str, username: str, password: str): def __init__(self, host: str, username: str, password: str):
if not host or not username or not password: if not host or not username or not password:
raise ValueError("host, username and password must be provided") raise ValueError("host, username and password must be provided")
@@ -11,6 +14,7 @@ class IpmiTool:
self.username = username self.username = username
self.password = password self.password = password
# 执行ipmitool命令并处理重试、超时和会话异常
def run_cmd(self, cmd: str) -> str: def run_cmd(self, cmd: str) -> str:
basecmd = f'ipmitool -H {self.host} -I lanplus -U {self.username} -P {self.password}' basecmd = f'ipmitool -H {self.host} -I lanplus -U {self.username} -P {self.password}'
command = f'{basecmd} {cmd}' command = f'{basecmd} {cmd}'
@@ -47,20 +51,23 @@ class IpmiTool:
else: else:
raise e raise e
# 读取iDRAC控制器基本信息
def mc_info(self) -> str: def mc_info(self) -> str:
""" """
execute ipmitool command mc info 执行ipmitool mc info命令
:return: :return:
""" """
return self.run_cmd(cmd='mc info') return self.run_cmd(cmd='mc info')
# 读取完整SDR传感器数据
def sensor(self) -> str: def sensor(self) -> str:
""" """
execute ipmitool command sdr to get sensor data 执行ipmitool sdr命令获取传感器数据
:return: :return:
""" """
return self.run_cmd(cmd='sdr') return self.run_cmd(cmd='sdr')
# 从SDR数据中解析温度传感器列表
def temperature(self, data: str = None) -> list: def temperature(self, data: str = None) -> list:
""" """
获取当前温度传感器列表 获取当前温度传感器列表
@@ -84,32 +91,30 @@ class IpmiTool:
return temperatures return temperatures
# 从SDR数据中解析风扇RPM列表
def fan_speeds(self) -> list: def fan_speeds(self) -> list:
""" """
get current fan speeds 获取当前风扇RPM列表
:return: list of fan speeds in percentage :return: 风扇RPM列表
""" """
data = self.sensor() data = self.sensor()
fan_speeds = [] fan_speeds = []
for line in data.splitlines(): for line in data.splitlines():
if 'Fan' in line and 'RPM' in line: if 'Fan' in line and 'RPM' in line:
# Extract numeric value from line - format is typically "Fan1 | 1234 | RPM |" # 从传感器行中提取RPM数值,典型格式为 "Fan1 RPM | 4800 RPM | ok"
parts = line.split('|') parts = line.split('|')
if len(parts) >= 2: if len(parts) >= 2:
try: try:
# Extract the value and convert RPM to percentage if possible
# For Dell servers, we may need to get duty cycle instead
value_str = parts[1].strip() value_str = parts[1].strip()
if value_str.isdigit(): if value_str.isdigit():
rpm = int(value_str) rpm = int(value_str)
# Placeholder: we might need to use raw commands to get duty cycle
# For now, return the raw value
fan_speeds.append(rpm) fan_speeds.append(rpm)
except ValueError: except ValueError:
continue continue
return fan_speeds return fan_speeds
# 获取当前风扇占空比,raw命令不可用时用RPM估算
def get_fan_duty_cycle(self, sensor_data: str = None) -> int: def get_fan_duty_cycle(self, sensor_data: str = None) -> int:
""" """
获取当前风扇占空比/百分比 获取当前风扇占空比/百分比
@@ -177,9 +182,10 @@ class IpmiTool:
return -1 # Return -1 if unable to determine return -1 # Return -1 if unable to determine
# 切换风扇自动/手动模式
def switch_fan_mode(self, auto: bool): def switch_fan_mode(self, auto: bool):
""" """
switch the fan mode 切换风扇模式
:param auto: :param auto:
:return: :return:
""" """
@@ -187,9 +193,10 @@ class IpmiTool:
auto_cmd = 'raw 0x30 0x30 0x01 0x01' auto_cmd = 'raw 0x30 0x30 0x01 0x01'
return self.run_cmd(cmd=auto_cmd) if auto else self.run_cmd(cmd=manual_cmd) return self.run_cmd(cmd=auto_cmd) if auto else self.run_cmd(cmd=manual_cmd)
# 设置手动风扇速度百分比
def set_fan_speed(self, speed: int): def set_fan_speed(self, speed: int):
""" """
set fan speed 设置风扇速度
:param speed: :param speed:
:return: :return:
""" """
+3
View File
@@ -5,7 +5,9 @@ logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
# 自定义日志格式器:统一输出北京时间,方便容器日志排查
class CustomFormatter(logging.Formatter): class CustomFormatter(logging.Formatter):
# 格式化日志记录并注入北京时间字段
def format(self, record): def format(self, record):
desired_timezone = timezone(timedelta(hours=8)) desired_timezone = timezone(timedelta(hours=8))
current_time = datetime.now(desired_timezone).strftime('%Y-%m-%d %H:%M:%S') current_time = datetime.now(desired_timezone).strftime('%Y-%m-%d %H:%M:%S')
@@ -13,6 +15,7 @@ class CustomFormatter(logging.Formatter):
return super().format(record) return super().format(record)
# 标准输出日志处理器:让Docker日志直接显示控制器运行状态
stream_handler = logging.StreamHandler() stream_handler = logging.StreamHandler()
stream_handler.setFormatter(CustomFormatter(' %(customtime)s [%(levelname)s] %(message)s')) stream_handler.setFormatter(CustomFormatter(' %(customtime)s [%(levelname)s] %(message)s'))
logger.addHandler(stream_handler) logger.addHandler(stream_handler)
+4 -8
View File
@@ -1,11 +1,7 @@
version: "3"
services: services:
dell-fans-controller-docker: dell-fans-controller:
image: lkddi/dell-fans-controller:latest image: lkddi/dell-fans-controller:latest
container_name: dell-fans-controller-docker container_name: dell-fans-controller
restart: always restart: always
environment: env_file:
HOST: 192.168.1.1 - .env
USERNAME: root
PASSWORD: password
+7 -3
View File
@@ -7,9 +7,10 @@ from controller.logger import logger
if __name__ == '__main__': if __name__ == '__main__':
host = os.getenv('HOST', "192.168.1.100") # 从环境变量读取iDRAC连接信息,开源版本不内置任何真实默认凭据
username = os.getenv('USERNAME', "root") host = os.getenv('HOST')
password = os.getenv('PASSWORD', "your_idrac_password") username = os.getenv('USERNAME')
password = os.getenv('PASSWORD')
if not host: if not host:
raise RuntimeError('未设置 HOST 环境变量') raise RuntimeError('未设置 HOST 环境变量')
@@ -19,14 +20,17 @@ if __name__ == '__main__':
if not password: if not password:
raise RuntimeError('未设置 PASSWORD 环境变量') raise RuntimeError('未设置 PASSWORD 环境变量')
# 复用控制器实例,避免每轮循环丢失上次设置状态
client = FanController(host=host, username=username, password=password) client = FanController(host=host, username=username, password=password)
while True: while True:
try: try:
# 执行一次温度读取和风扇控制周期
client.run() client.run()
time.sleep(60) time.sleep(60)
except Exception as err: except Exception as err:
logger.error( logger.error(
f'运行控制器失败 {err}. {traceback.format_exc()}' f'运行控制器失败 {err}. {traceback.format_exc()}'
) )
# iDRAC会话异常时等待下一轮,避免连续请求压垮IPMI服务
time.sleep(60) time.sleep(60)