基本部署就不说了,只是想说一下部署时添加用户密码。
管理用户有多种方式,关系数据库就很简单,按照配置就好。这里说一下使用内置数据库。需要开启内置数据库。然后使用一个py将用户密码初始化导入。减少部署维护。
XML
version: '3.8'
services:
emqx:
image: emqx/emqx:5.7.0 # 固定版本,避免自动更新
container_name: emqx
restart: always
ports:
- "1883:1883" # MQTT TCP
- "8083:8083" # MQTT WebSocket
- "8883:8883" # MQTT SSL
- "8084:8084" # MQTT WebSocket SSL
- "18083:18083" # Dashboard
volumes:
- /opt/emqx/data:/opt/emqx/data
- /opt/emqx/log:/opt/emqx/log
- /opt/emqx/conf:/opt/emqx/etc/emqx.conf.d # 自定义配置挂载
environment:
# 核心:启用内置数据库认证
- EMQX_AUTHENTICATION__1__MECHANISM=password_based
- EMQX_AUTHENTICATION__1__BACKEND=built_in_database
- EMQX_AUTHENTICATION__1__ENABLE=true
# 默认管理员账户(Dashboard 登录)
- EMQX_DEFAULT_USER__USERNAME=admin
- EMQX_DEFAULT_USER__PASSWORD=admin123 # 替换为你的管理员密码
# 可选:调整最大连接数、日志级别
- EMQX_LISTENER__TCP__DEFAULT__MAX_CONNECTIONS=1024000
- EMQX_LOG__LEVEL=info
networks:
- emqx-network
networks:
emqx-network:
driver: bridge
在部署时简化配置,可以用另外一个程序直接导入用户名密码。其实就是直接调用emqx提供的api。
python
#!/usr/bin/env python3
"""
EMQX用户批量导入工具 - 重试退出版
登录失败重试3次,还不成功就异常退出
"""
import csv
import time
import sys
import os
import requests
# ============ 配置 ============
EMQX_HOST = os.getenv('EMQX_HOST', 'emqx')
EMQX_PORT = os.getenv('EMQX_PORT', '18083')
BASE_URL = f"http://{EMQX_HOST}:{EMQX_PORT}/api/v5"
LOGIN_URL = f"{BASE_URL}/login"
USER_API = f"{BASE_URL}/authentication/password_based%3Abuilt_in_database/users"
ADMIN_USER = os.getenv('ADMIN_USER', 'admin')
ADMIN_PASS = os.getenv('ADMIN_PASS', 'mq_Admin@2025!Jetlinks#124')
CSV_FILE = os.getenv('CSV_FILE', '/app/users.csv')
# =============================
def fatal_error(message):
"""致命错误,直接退出"""
print(f"💥 {message}")
sys.exit(1)
def login_with_retry():
"""登录重试3次,失败就退出"""
print("🔑 登录EMQX...")
for attempt in range(1, 4): # 重试3次
print(f" 尝试 {attempt}/3...")
try:
response = requests.post(
LOGIN_URL,
json={"username": ADMIN_USER, "password": ADMIN_PASS},
headers={"Content-Type": "application/json"},
timeout=10,
verify=False
)
if response.status_code == 200:
token = response.json().get("token")
if token:
print("✅ 登录成功")
return token
else:
print("❌ 响应中没有Token")
else:
print(f"❌ 登录失败: HTTP {response.status_code}")
except Exception as e:
print(f"❌ 连接异常: {e}")
# 如果不是最后一次尝试,等待5秒
if attempt < 3:
print("⏳ 等待5秒后重试...")
time.sleep(5)
# 3次都失败,退出
fatal_error("登录失败,等待容器重启")
def import_users():
"""主导入函数"""
print("=" * 60)
print("EMQX用户批量导入")
print("=" * 60)
print(f"服务器: {EMQX_HOST}:{EMQX_PORT}")
print(f"管理员: {ADMIN_USER}")
print("=" * 60)
# 1. 登录(失败就退出)
token = login_with_retry()
# 2. 读取CSV
users = []
try:
with open(CSV_FILE, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if not is_header:
# 检查是否包含表头关键词
username_headers = {'用户', '用户名', 'user', 'username', 'userid'}
password_headers = {'密码', 'password'}
superuser_headers = {'超级用户', 'is_superuser', 'superuser', '管理员'}
parts = [p.strip().lower() for p in line.split(',')]
# 检查是否同时包含用户名和密码表头
has_username_header = any(h in parts for h in username_headers)
has_password_header = any(h in parts for h in password_headers)
if has_username_header and has_password_header:
# 这是表头行,跳过不处理
print(f"📋 检测到表头: {line}")
is_header = True
# 确定各字段的位置
for i, header in enumerate(parts):
if header in username_headers:
username_idx = i
elif header in password_headers:
password_idx = i
elif header in superuser_headers:
superuser_idx = i
continue # 跳过表头行,继续读取下一行
parts = line.split(',')
if len(parts) < 2:
continue
username = parts[0].strip()
password = parts[1].strip()
if not username or not password:
continue
is_super = False
if len(parts) > 2:
flag = parts[2].strip().lower()
is_super = flag in ['true', '1', 'yes', 'y']
users.append({
'user_id': username,
'password': password,
'is_superuser': is_super
})
if not users:
print("❌ CSV中没有用户")
return True # 没有用户不算失败
print(f"📄 读取到 {len(users)} 个用户")
except FileNotFoundError:
fatal_error(f"找不到CSV文件: {CSV_FILE}")
except Exception as e:
fatal_error(f"读取CSV失败: {e}")
# 3. 导入用户
print(f"\n🚀 开始导入...")
success = 0
failed = 0
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
for i, user in enumerate(users, 1):
username = user['user_id']
print(f"[{i:3}/{len(users)}] {username:20}", end="", flush=True)
try:
response = requests.post(
USER_API,
json=user,
headers=headers,
timeout=30,
verify=False
)
if response.status_code in [200, 201, 409]:
print(" ✅ 成功")
success += 1
else:
print(f" ❌ HTTP {response.status_code}")
failed += 1
# 创建用户失败不退出,继续下一个
except Exception as e:
print(f" ❌ 错误: {str(e)[:30]}")
failed += 1
# 网络错误也不退出,继续下一个
if i < len(users):
time.sleep(0.3)
# 4. 显示结果
print(f"\n" + "=" * 60)
print("📊 导入完成!")
print(f" 总计: {len(users)}")
print(f" 成功: {success} ✅")
print(f" 失败: {failed} ❌")
if success > 0:
print(f"\n💡 验证: http://{EMQX_HOST}:{EMQX_PORT}")
return failed == 0
if __name__ == "__main__":
try:
success = import_users()
if success:
print("\n🎉 导入成功!")
sys.exit(0)
else:
print("\n⚠️ 部分用户导入失败")
sys.exit(1) # 有失败,退出码1,触发容器重启
except KeyboardInterrupt:
print("\n❌ 用户中断")
sys.exit(130)
except SystemExit as e:
# 重新抛出SystemExit
raise
except Exception as e:
print(f"\n💥 未处理异常: {e}")
sys.exit(1) # 异常退出,触发容器重启
py生成一个镜像,部署时自动执行一次,就将user.csv文件中用户密码导入了。