首页
统计信息
友情链接
壁纸
Search
1
【更新】CommentToMail typecho2017&v4.1& Mailer三版本,支持php5.6/7,插件详解
158,336 阅读
2
【90APT开源免费】第三代哈弗H6、哈弗大狗、H6经典版、坦克300华阳安波福车机开启无线ADB、升级地图、安装软件全流程
37,821 阅读
3
CentOS 7安装bbr教程
13,032 阅读
4
深信服超融合架构测试介绍
12,645 阅读
5
纯小白10分钟变身linux建站高手?宝塔linux面板全体验
12,618 阅读
技术相关
ACG相关
胡言乱语
数码杂烩
登录
Search
标签搜索
进击的巨人
漫画
宝塔
php
typecho
diy
vps
折腾
动漫
优酷路由宝
ubuntu
路由器
QQ
KMS
王忘杰
累计撰写
306
篇文章
累计收到
179
条评论
首页
栏目
技术相关
ACG相关
胡言乱语
数码杂烩
页面
统计信息
友情链接
壁纸
搜索到
274
篇与
的结果
2025-12-18
AI开发SMB文件复制工具 开机自启 后台运行 定时任务 密码加密
import tkinter as tk from tkinter import ttk, messagebox, filedialog, scrolledtext import os import threading import schedule import time from datetime import datetime, date, timedelta from smb.SMBConnection import SMBConnection import socket import json import sys import io import base64 from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import pystray from PIL import Image, ImageDraw import winreg import atexit class SMBCopierApp: def __init__(self, root): self.root = root self.root.title("SMB文件复制工具") self.root.geometry("850x750") self.root.minsize(800, 600) # 系统托盘图标 self.tray_icon = None self.in_background = False # 存储定时任务 self.scheduled_tasks = [] self.schedule_thread = None self.running = False # 上一次执行时间,避免重复执行 self.last_execution_time = None self.last_execution_day = None # 配置文件路径 self.config_file = "smb_copier_config.json" # 密钥文件路径 self.key_file = "smb_copier.key" # 加密密钥(从文件加载或生成新的) self.encryption_key = self.load_or_create_key() # 创建主容器 self.main_container = ttk.PanedWindow(root, orient=tk.VERTICAL) self.main_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建上部分(设置面板) self.settings_frame = ttk.Frame(self.main_container) self.main_container.add(self.settings_frame, weight=1) # 创建下部分(日志面板) self.log_frame = ttk.Frame(self.main_container) self.main_container.add(self.log_frame, weight=1) self.setup_settings_ui() self.setup_log_ui() self.load_config() self.start_schedule_thread() # 设置关闭事件 self.root.protocol("WM_DELETE_WINDOW", self.on_closing) # 注册退出清理 atexit.register(self.cleanup) def generate_key(self): """生成一个新的加密密钥""" try: # 使用用户密码生成密钥 password = "smb_copier_default_password".encode() # 你可以修改这个默认密码 salt = b'smb_copier_salt' # 盐值,用于密钥派生 # 使用PBKDF2派生密钥 kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, ) key = base64.urlsafe_b64encode(kdf.derive(password)) # 保存密钥到文件 with open(self.key_file, 'wb') as f: f.write(key) self.log("已生成新的加密密钥") return key except Exception as e: self.log(f"生成密钥失败: {str(e)}") # 如果失败,使用一个简单的备用密钥 return base64.urlsafe_b64encode(b'smb-copier-default-key-12345678') def load_or_create_key(self): """加载或创建加密密钥""" try: if os.path.exists(self.key_file): with open(self.key_file, 'rb') as f: key = f.read() self.log("已加载现有的加密密钥") return key else: return self.generate_key() except Exception as e: self.log(f"加载密钥失败: {str(e)},使用备用密钥") # 使用一个简单的备用密钥 return base64.urlsafe_b64encode(b'smb-copier-default-key-12345678') def encrypt_password(self, password): """加密密码""" if not password: return "" try: fernet = Fernet(self.encryption_key) encrypted = fernet.encrypt(password.encode()) return encrypted.decode('utf-8') except Exception as e: self.log(f"密码加密失败: {str(e)}") return password # 如果加密失败,返回原始密码 def decrypt_password(self, encrypted_password): """解密密码""" if not encrypted_password: return "" try: fernet = Fernet(self.encryption_key) decrypted = fernet.decrypt(encrypted_password.encode()) return decrypted.decode('utf-8') except Exception as e: # 如果解密失败,可能是旧版本的未加密密码 self.log(f"密码解密失败,可能为未加密密码: {str(e)}") return encrypted_password # 返回原始值(可能是未加密的密码) def setup_settings_ui(self): """设置上部分的界面""" # 创建滚动框架 self.settings_canvas = tk.Canvas(self.settings_frame, highlightthickness=0) self.settings_scrollbar = ttk.Scrollbar(self.settings_frame, orient=tk.VERTICAL, command=self.settings_canvas.yview) self.settings_inner_frame = ttk.Frame(self.settings_canvas) # 配置滚动 self.settings_canvas.configure(yscrollcommand=self.settings_scrollbar.set) self.settings_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.settings_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 将内部框架添加到画布 self.canvas_window = self.settings_canvas.create_window((0, 0), window=self.settings_inner_frame, anchor=tk.NW) # 绑定事件 self.settings_inner_frame.bind('<Configure>', self.on_settings_frame_configure) self.settings_canvas.bind('<Configure>', self.on_settings_canvas_configure) # 使用Notebook选项卡组织内容 self.notebook = ttk.Notebook(self.settings_inner_frame) self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建各个选项卡 self.setup_source_tab() self.setup_target_tab() self.setup_copy_tab() self.setup_schedule_tab() def on_settings_frame_configure(self, event=None): """内部框架配置时调整画布滚动区域""" self.settings_canvas.configure(scrollregion=self.settings_canvas.bbox('all')) def on_settings_canvas_configure(self, event): """画布大小变化时调整内部框架宽度""" self.settings_canvas.itemconfig(self.canvas_window, width=event.width) def setup_log_ui(self): """设置下部分的日志界面""" # 日志标题和清除按钮 log_header = ttk.Frame(self.log_frame) log_header.pack(fill=tk.X, padx=5, pady=(5, 0)) ttk.Label(log_header, text="操作日志", font=('Arial', 10, 'bold')).pack(side=tk.LEFT) # 添加密钥管理按钮 ttk.Button(log_header, text="重新生成密钥", command=self.regenerate_key, width=12).pack(side=tk.RIGHT, padx=(0, 5)) ttk.Button(log_header, text="清除日志", command=self.clear_log, width=10).pack(side=tk.RIGHT) # 日志文本框 self.log_text = scrolledtext.ScrolledText( self.log_frame, wrap=tk.WORD, height=12, font=('Consolas', 9) ) self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 添加初始日志 self.log("SMB文件复制工具已启动") self.log(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") def regenerate_key(self): """重新生成加密密钥""" if messagebox.askyesno("确认", "重新生成密钥将导致已保存的加密密码无法解密,确定要继续吗?"): try: # 删除旧的密钥文件 if os.path.exists(self.key_file): os.remove(self.key_file) self.log("已删除旧的密钥文件") # 生成新的密钥 self.encryption_key = self.generate_key() # 清除配置文件中的加密密码 config = {} if os.path.exists(self.config_file): with open(self.config_file, 'r', encoding='utf-8') as f: config = json.load(f) # 清除加密的密码字段 password_fields = ['source_smb_password', 'target_smb_password'] for field in password_fields: if field in config: config[field] = "" # 保存配置 with open(self.config_file, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=2) # 清除界面中的密码 self.source_smb_password.set("") self.target_smb_password.set("") self.log("已重新生成加密密钥,请重新输入并保存密码") messagebox.showinfo("成功", "加密密钥已重新生成,请重新输入并保存密码。") except Exception as e: self.log(f"重新生成密钥失败: {str(e)}") messagebox.showerror("错误", f"重新生成密钥失败: {str(e)}") def setup_source_tab(self): """设置源目录选项卡""" self.source_tab = ttk.Frame(self.notebook) self.notebook.add(self.source_tab, text="源目录") # 源目录类型选择 self.source_type = tk.StringVar(value="local") type_frame = ttk.LabelFrame(self.source_tab, text="目录类型", padding="10") type_frame.pack(fill=tk.X, padx=10, pady=(10, 5)) ttk.Radiobutton(type_frame, text="本地目录", variable=self.source_type, value="local", command=self.toggle_source_type).pack(side=tk.LEFT, padx=(0, 20)) ttk.Radiobutton(type_frame, text="SMB目录", variable=self.source_type, value="smb", command=self.toggle_source_type).pack(side=tk.LEFT) # 本地源目录 self.source_local_frame = ttk.LabelFrame(self.source_tab, text="本地目录设置", padding="10") self.source_local_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(self.source_local_frame, text="目录路径:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5)) self.source_local_path = tk.StringVar() source_entry = ttk.Entry(self.source_local_frame, textvariable=self.source_local_path) source_entry.grid(row=0, column=1, sticky=tk.EW, padx=(5, 5), pady=(0, 5)) ttk.Button(self.source_local_frame, text="浏览", command=self.browse_source_local, width=8).grid(row=0, column=2, pady=(0, 5)) # 配置网格权重 self.source_local_frame.columnconfigure(1, weight=1) # SMB源目录设置 self.source_smb_frame = ttk.LabelFrame(self.source_tab, text="SMB目录设置", padding="10") self.source_smb_frame.pack(fill=tk.X, padx=10, pady=5) # SMB路径 ttk.Label(self.source_smb_frame, text="SMB路径:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5)) self.source_smb_path = tk.StringVar() smb_entry = ttk.Entry(self.source_smb_frame, textvariable=self.source_smb_path) smb_entry.grid(row=0, column=1, sticky=tk.EW, padx=(5, 5), pady=(0, 5)) # 用户名和密码 ttk.Label(self.source_smb_frame, text="用户名:").grid(row=1, column=0, sticky=tk.W, pady=(0, 5)) self.source_smb_username = tk.StringVar() ttk.Entry(self.source_smb_frame, textvariable=self.source_smb_username, width=20).grid(row=1, column=1, sticky=tk.W, padx=(5, 5), pady=(0, 5)) ttk.Label(self.source_smb_frame, text="密码:").grid(row=1, column=2, sticky=tk.W, pady=(0, 5)) self.source_smb_password = tk.StringVar() ttk.Entry(self.source_smb_frame, textvariable=self.source_smb_password, show="*", width=20).grid(row=1, column=3, sticky=tk.W, padx=(5, 0), pady=(0, 5)) # 显示密码复选框 self.show_source_password = tk.BooleanVar(value=False) ttk.Checkbutton(self.source_smb_frame, text="显示密码", variable=self.show_source_password, command=self.toggle_source_password_visibility).grid(row=2, column=3, sticky=tk.W, pady=(0, 5)) # 测试连接按钮 ttk.Button(self.source_smb_frame, text="测试连接", command=self.test_source_smb_connection).grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=(0, 5)) # 示例标签 example_frame = ttk.Frame(self.source_smb_frame) example_frame.grid(row=3, column=0, columnspan=4, sticky=tk.W, pady=(5, 0)) ttk.Label(example_frame, text="示例格式: \\\\192.168.1.1\\share\\目录", foreground="gray", font=('Arial', 8)).pack(side=tk.LEFT) # 配置网格权重 self.source_smb_frame.columnconfigure(1, weight=1) # 初始化UI状态 self.toggle_source_type() def toggle_source_password_visibility(self): """切换源密码显示状态""" if self.show_source_password.get(): self.source_smb_frame.winfo_children()[6].config(show="") # 密码输入框 else: self.source_smb_frame.winfo_children()[6].config(show="*") def toggle_target_password_visibility(self): """切换目标密码显示状态""" if self.show_target_password.get(): self.target_smb_frame.winfo_children()[6].config(show="") # 密码输入框 else: self.target_smb_frame.winfo_children()[6].config(show="*") def setup_target_tab(self): """设置目标目录选项卡""" self.target_tab = ttk.Frame(self.notebook) self.notebook.add(self.target_tab, text="目标目录") # 目标目录类型选择 self.target_type = tk.StringVar(value="local") type_frame = ttk.LabelFrame(self.target_tab, text="目录类型", padding="10") type_frame.pack(fill=tk.X, padx=10, pady=(10, 5)) ttk.Radiobutton(type_frame, text="本地目录", variable=self.target_type, value="local", command=self.toggle_target_type).pack(side=tk.LEFT, padx=(0, 20)) ttk.Radiobutton(type_frame, text="SMB目录", variable=self.target_type, value="smb", command=self.toggle_target_type).pack(side=tk.LEFT) # 本地目标目录 self.target_local_frame = ttk.LabelFrame(self.target_tab, text="本地目录设置", padding="10") self.target_local_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(self.target_local_frame, text="目录路径:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5)) self.target_local_path = tk.StringVar() target_entry = ttk.Entry(self.target_local_frame, textvariable=self.target_local_path) target_entry.grid(row=0, column=1, sticky=tk.EW, padx=(5, 5), pady=(0, 5)) ttk.Button(self.target_local_frame, text="浏览", command=self.browse_target_local, width=8).grid(row=0, column=2, pady=(0, 5)) # 配置网格权重 self.target_local_frame.columnconfigure(1, weight=1) # SMB目标目录设置 self.target_smb_frame = ttk.LabelFrame(self.target_tab, text="SMB目录设置", padding="10") self.target_smb_frame.pack(fill=tk.X, padx=10, pady=5) # SMB路径 ttk.Label(self.target_smb_frame, text="SMB路径:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5)) self.target_smb_path = tk.StringVar() target_smb_entry = ttk.Entry(self.target_smb_frame, textvariable=self.target_smb_path) target_smb_entry.grid(row=0, column=1, sticky=tk.EW, padx=(5, 5), pady=(0, 5)) # 用户名和密码 ttk.Label(self.target_smb_frame, text="用户名:").grid(row=1, column=0, sticky=tk.W, pady=(0, 5)) self.target_smb_username = tk.StringVar() ttk.Entry(self.target_smb_frame, textvariable=self.target_smb_username, width=20).grid(row=1, column=1, sticky=tk.W, padx=(5, 5), pady=(0, 5)) ttk.Label(self.target_smb_frame, text="密码:").grid(row=1, column=2, sticky=tk.W, pady=(0, 5)) self.target_smb_password = tk.StringVar() ttk.Entry(self.target_smb_frame, textvariable=self.target_smb_password, show="*", width=20).grid(row=1, column=3, sticky=tk.W, padx=(5, 0), pady=(0, 5)) # 显示密码复选框 self.show_target_password = tk.BooleanVar(value=False) ttk.Checkbutton(self.target_smb_frame, text="显示密码", variable=self.show_target_password, command=self.toggle_target_password_visibility).grid(row=2, column=3, sticky=tk.W, pady=(0, 5)) # 测试连接按钮 ttk.Button(self.target_smb_frame, text="测试连接", command=self.test_target_smb_connection).grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=(0, 5)) # 示例标签 example_frame = ttk.Frame(self.target_smb_frame) example_frame.grid(row=3, column=0, columnspan=4, sticky=tk.W, pady=(5, 0)) ttk.Label(example_frame, text="示例格式: \\\\192.168.1.1\\share\\目录", foreground="gray", font=('Arial', 8)).pack(side=tk.LEFT) # 配置网格权重 self.target_smb_frame.columnconfigure(1, weight=1) # 初始化UI状态 self.toggle_target_type() def toggle_source_type(self): """切换源目录类型""" if self.source_type.get() == "local": self.source_smb_frame.pack_forget() self.source_local_frame.pack(fill=tk.X, padx=10, pady=5) else: self.source_local_frame.pack_forget() self.source_smb_frame.pack(fill=tk.X, padx=10, pady=5) def toggle_target_type(self): """切换目标目录类型""" if self.target_type.get() == "local": self.target_smb_frame.pack_forget() self.target_local_frame.pack(fill=tk.X, padx=10, pady=5) else: self.target_local_frame.pack_forget() self.target_smb_frame.pack(fill=tk.X, padx=10, pady=5) def setup_copy_tab(self): """设置复制选项选项卡""" self.copy_tab = ttk.Frame(self.notebook) self.notebook.add(self.copy_tab, text="复制选项") # 复制模式 copy_frame = ttk.LabelFrame(self.copy_tab, text="复制模式", padding="15") copy_frame.pack(fill=tk.X, padx=10, pady=(10, 15)) self.copy_mode = tk.StringVar(value="overwrite") ttk.Radiobutton(copy_frame, text="覆盖 - 如果文件已存在则覆盖", variable=self.copy_mode, value="overwrite").pack(anchor=tk.W, pady=(0, 5)) ttk.Radiobutton(copy_frame, text="忽略 - 如果文件已存在则跳过", variable=self.copy_mode, value="skip").pack(anchor=tk.W) # 开机自启选项 auto_start_frame = ttk.LabelFrame(self.copy_tab, text="程序设置", padding="15") auto_start_frame.pack(fill=tk.X, padx=10, pady=(0, 15)) # 开机自启 self.auto_start = tk.BooleanVar(value=False) auto_start_check = ttk.Checkbutton(auto_start_frame, text="开机自启", variable=self.auto_start, command=self.toggle_auto_start) auto_start_check.pack(anchor=tk.W, pady=(0, 8)) # 后台运行 self.background_mode = tk.BooleanVar(value=False) background_check = ttk.Checkbutton(auto_start_frame, text="后台运行(最小化到系统托盘)", variable=self.background_mode, command=self.toggle_background_mode) background_check.pack(anchor=tk.W) # 立即复制按钮 btn_frame = ttk.Frame(self.copy_tab) btn_frame.pack(fill=tk.X, padx=10, pady=(0, 10)) ttk.Button(btn_frame, text="立即执行复制", command=self.execute_copy_now, style="Accent.TButton").pack(side=tk.LEFT, padx=(0, 10)) ttk.Button(btn_frame, text="保存配置", command=self.save_config).pack(side=tk.LEFT) def toggle_auto_start(self): """切换开机自启""" try: if self.auto_start.get(): # 设置开机自启 self.set_auto_start(True) self.log("已启用开机自启") else: # 取消开机自启 self.set_auto_start(False) self.log("已禁用开机自启") except Exception as e: self.log(f"设置开机自启失败: {str(e)}") messagebox.showerror("错误", f"设置开机自启失败: {str(e)}") def set_auto_start(self, enable=True): """设置开机自启""" try: # 获取当前可执行文件路径 if getattr(sys, 'frozen', False): # 如果是打包后的exe exe_path = sys.executable else: # 如果是Python脚本 exe_path = sys.executable script_path = os.path.abspath(__file__) exe_path = f'"{exe_path}" "{script_path}"' if sys.platform == "win32": # Windows系统 - 使用注册表 key = winreg.HKEY_CURRENT_USER subkey = r"Software\Microsoft\Windows\CurrentVersion\Run" try: with winreg.OpenKey(key, subkey, 0, winreg.KEY_SET_VALUE) as reg_key: if enable: winreg.SetValueEx(reg_key, "SMBCopier", 0, winreg.REG_SZ, exe_path) else: try: winreg.DeleteValue(reg_key, "SMBCopier") except: pass # 如果键不存在,忽略 except Exception as e: self.log(f"访问注册表失败: {str(e)}") else: # Linux/Mac系统 - 使用启动文件夹 startup_dir = "" if sys.platform == "darwin": # Mac startup_dir = os.path.expanduser("~/Library/LaunchAgents") else: # Linux startup_dir = os.path.expanduser("~/.config/autostart") if startup_dir: os.makedirs(startup_dir, exist_ok=True) desktop_file = os.path.join(startup_dir, "smb_copier.desktop") if enable: desktop_content = f"""[Desktop Entry] Type=Application Name=SMB Copier Exec={exe_path} Hidden=false X-GNOME-Autostart-enabled=true """ with open(desktop_file, "w") as f: f.write(desktop_content) else: if os.path.exists(desktop_file): os.remove(desktop_file) except Exception as e: raise Exception(f"设置开机自启失败: {str(e)}") def toggle_background_mode(self): """切换后台运行模式""" if self.background_mode.get(): self.log("已启用后台运行模式") if not self.tray_icon: self.create_tray_icon() else: self.log("已禁用后台运行模式") # 如果正在后台运行,则恢复窗口 if self.in_background: self.restore_from_tray() def create_tray_icon(self): """创建系统托盘图标""" try: # 创建图标 image = Image.new('RGB', (64, 64), color='white') draw = ImageDraw.Draw(image) # 绘制一个简单的图标 draw.rectangle([16, 16, 48, 48], outline='blue', width=3) draw.line([20, 20, 44, 44], fill='green', width=3) draw.line([20, 44, 44, 20], fill='red', width=3) # 创建菜单 menu = ( pystray.MenuItem("显示窗口", self.restore_from_tray), pystray.MenuItem("立即复制", self.execute_copy_now), pystray.MenuItem("退出", self.quit_from_tray) ) # 创建托盘图标 self.tray_icon = pystray.Icon("smb_copier", image, "SMB文件复制工具", menu) # 启动托盘图标线程 threading.Thread(target=self.tray_icon.run, daemon=True).start() self.log("系统托盘图标已创建") except Exception as e: self.log(f"创建系统托盘图标失败: {str(e)}") messagebox.showwarning("警告", f"创建系统托盘图标失败: {str(e)}\n请确保已安装PIL和pystray库。") def restore_from_tray(self): """从托盘恢复窗口""" if self.tray_icon: self.root.after(0, self._show_window) def _show_window(self): """显示窗口""" self.root.deiconify() # 显示窗口 self.root.lift() # 置于顶层 self.root.focus_force() # 获取焦点 self.in_background = False def quit_from_tray(self): """从托盘退出程序""" if self.tray_icon: self.tray_icon.stop() self.root.after(0, self.root.quit) def minimize_to_tray(self): """最小化到系统托盘""" if self.tray_icon: self.root.withdraw() # 隐藏窗口 self.in_background = True self.log("程序已最小化到系统托盘") def browse_source_local(self): """浏览本地源目录""" path = filedialog.askdirectory(title="选择源目录") if path: self.source_local_path.set(path) self.log(f"源目录设置为: {path}") def browse_target_local(self): """浏览本地目标目录""" path = filedialog.askdirectory(title="选择目标目录") if path: self.target_local_path.set(path) self.log(f"目标目录设置为: {path}") def parse_smb_path(self, smb_path): """解析SMB路径""" if not smb_path: return None, None, None # 统一替换为斜杠 smb_path = smb_path.replace('\\', '/') # 移除开头的双斜杠或双反斜杠 if smb_path.startswith('//'): smb_path = smb_path[2:] # 分割路径 parts = smb_path.split('/', 2) if len(parts) < 2: return None, None, None server = parts[0] share = parts[1] path = parts[2] if len(parts) > 2 else '' # 确保路径以/开头 if path and not path.startswith('/'): path = '/' + path return server, share, path def test_source_smb_connection(self): """测试源SMB连接""" smb_path = self.source_smb_path.get() if not smb_path: messagebox.showwarning("警告", "请输入SMB路径") return server, share, path = self.parse_smb_path(smb_path) if not server or not share: messagebox.showwarning("警告", "SMB路径格式不正确,请参考示例格式") return username = self.source_smb_username.get() password = self.source_smb_password.get() self.test_smb_connection(server, share, username, password, "源") def test_target_smb_connection(self): """测试目标SMB连接""" smb_path = self.target_smb_path.get() if not smb_path: messagebox.showwarning("警告", "请输入SMB路径") return server, share, path = self.parse_smb_path(smb_path) if not server or not share: messagebox.showwarning("警告", "SMB路径格式不正确,请参考示例格式") return username = self.target_smb_username.get() password = self.target_smb_password.get() self.test_smb_connection(server, share, username, password, "目标") def test_smb_connection(self, server, share, username, password, label): """测试SMB连接""" try: self.log(f"正在测试{label}SMB连接: {server}/{share}") # 创建SMB连接 conn = SMBConnection( username if username else '', password if password else '', socket.gethostname(), server, domain='', use_ntlm_v2=True ) # 尝试连接 connected = conn.connect(server, 139) if connected: # 尝试列出共享内容 try: conn.listPath(share, '/') self.log(f"✓ {label}SMB连接测试成功") messagebox.showinfo("成功", f"{label}SMB连接测试成功!") except Exception as e: error_msg = f"无法访问共享目录: {str(e)}" self.log(f"✗ {error_msg}") messagebox.showerror("错误", error_msg) finally: conn.close() else: error_msg = f"无法连接到{label}SMB服务器" self.log(f"✗ {error_msg}") messagebox.showerror("错误", error_msg) except Exception as e: error_msg = f"{label}SMB连接测试失败: {str(e)}" self.log(f"✗ {error_msg}") messagebox.showerror("错误", error_msg) def setup_schedule_tab(self): """设置定时任务选项卡""" self.schedule_tab = ttk.Frame(self.notebook) self.notebook.add(self.schedule_tab, text="定时任务") # 启用定时任务 schedule_frame = ttk.LabelFrame(self.schedule_tab, text="定时设置", padding="15") schedule_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) self.enable_schedule = tk.BooleanVar(value=False) ttk.Checkbutton(schedule_frame, text="启用定时任务", variable=self.enable_schedule).pack(anchor=tk.W, pady=(0, 15)) # 频率和时间设置 settings_frame = ttk.Frame(schedule_frame) settings_frame.pack(fill=tk.X, pady=(0, 15)) # 频率选择 freq_frame = ttk.Frame(settings_frame) freq_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 20)) ttk.Label(freq_frame, text="频率:").pack(anchor=tk.W) self.schedule_freq = tk.StringVar(value="每天") freq_combo = ttk.Combobox(freq_frame, textvariable=self.schedule_freq, values=["每天", "每周"], width=10, state="readonly") freq_combo.pack(anchor=tk.W, pady=(5, 0)) # 每周选择 self.weekday_frame = ttk.Frame(freq_frame) self.weekday_frame.pack(anchor=tk.W, pady=(5, 0)) ttk.Label(self.weekday_frame, text="星期:").pack(side=tk.LEFT) self.weekday = tk.StringVar(value="星期一") weekday_combo = ttk.Combobox(self.weekday_frame, textvariable=self.weekday, values=["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], width=10, state="readonly") weekday_combo.pack(side=tk.LEFT, padx=(5, 0)) self.weekday_frame.pack_forget() # 默认隐藏 # 时间选择 time_frame = ttk.Frame(settings_frame) time_frame.pack(side=tk.LEFT, fill=tk.Y) ttk.Label(time_frame, text="时间:").pack(anchor=tk.W) time_input_frame = ttk.Frame(time_frame) time_input_frame.pack(anchor=tk.W, pady=(5, 0)) self.schedule_hour = tk.StringVar(value="00") hour_combo = ttk.Combobox(time_input_frame, textvariable=self.schedule_hour, values=[f"{i:02d}" for i in range(24)], width=5, state="readonly") hour_combo.pack(side=tk.LEFT) ttk.Label(time_input_frame, text=":").pack(side=tk.LEFT, padx=(2, 2)) self.schedule_minute = tk.StringVar(value="00") minute_combo = ttk.Combobox(time_input_frame, textvariable=self.schedule_minute, values=[f"{i:02d}" for i in range(60)], width=5, state="readonly") minute_combo.pack(side=tk.LEFT) # 绑定频率变化事件 freq_combo.bind('<<ComboboxSelected>>', self.on_freq_change) # 添加定时任务按钮 ttk.Button(schedule_frame, text="添加定时任务", command=self.add_schedule_task).pack(anchor=tk.W, pady=(0, 15)) # 定时任务列表 list_frame = ttk.Frame(schedule_frame) list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15)) # 创建滚动条 list_scrollbar = ttk.Scrollbar(list_frame) list_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 创建列表框 self.task_listbox = tk.Listbox(list_frame, height=8, yscrollcommand=list_scrollbar.set) self.task_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) list_scrollbar.config(command=self.task_listbox.yview) # 删除任务按钮 ttk.Button(schedule_frame, text="删除选中任务", command=self.delete_selected_task).pack(anchor=tk.W, pady=(0, 5)) def on_freq_change(self, event=None): """频率选择变化""" if self.schedule_freq.get() == "每周": self.weekday_frame.pack(anchor=tk.W, pady=(5, 0)) else: self.weekday_frame.pack_forget() def add_schedule_task(self): """添加定时任务""" if not self.enable_schedule.get(): messagebox.showwarning("警告", "请先启用定时任务") return # 获取频率和时间 freq = self.schedule_freq.get() hour = self.schedule_hour.get() minute = self.schedule_minute.get() if freq == "每周": weekday = self.weekday.get() weekday_num = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"].index(weekday) task_desc = f"每周{weekday} {hour}:{minute}" task_data = { 'freq': 'weekly', 'weekday': weekday_num, 'hour': int(hour), 'minute': int(minute) } else: task_desc = f"每天 {hour}:{minute}" task_data = { 'freq': 'daily', 'hour': int(hour), 'minute': int(minute) } # 添加到列表 self.task_listbox.insert(tk.END, task_desc) self.scheduled_tasks.append(task_data) # 记录日志 self.log(f"已添加定时任务: {task_desc}") def delete_selected_task(self): """删除选中的定时任务""" selection = self.task_listbox.curselection() if not selection: messagebox.showwarning("警告", "请先选择一个任务") return index = selection[0] task_desc = self.task_listbox.get(index) self.task_listbox.delete(index) del self.scheduled_tasks[index] self.log(f"已删除定时任务: {task_desc}") def execute_copy_now(self): """立即执行复制""" # 在新线程中执行复制,避免界面卡顿 thread = threading.Thread(target=self.copy_files, daemon=True) thread.start() def copy_files(self): """执行文件复制""" try: self.log("=" * 60) self.log("开始复制文件...") start_time = datetime.now() self.log(f"开始时间: {start_time.strftime('%H:%M:%S')}") # 获取源文件和目标文件列表 source_files = self.get_source_files() if not source_files: self.log("警告: 没有找到要复制的文件") return self.log(f"找到 {len(source_files)} 个文件需要复制") # 执行复制 copied = 0 skipped = 0 errors = 0 for i, file_info in enumerate(source_files): try: success = self.copy_single_file(file_info) if success == "copied": copied += 1 if copied % 10 == 0: # 每10个文件记录一次进度 self.log(f"进度: 已处理 {i+1}/{len(source_files)} 个文件") elif success == "skipped": skipped += 1 elif success == "error": errors += 1 self.log(f"错误: 复制失败 - {file_info.get('rel_path', '未知文件')}") except Exception as e: errors += 1 self.log(f"异常: 复制文件时出错 - {str(e)}") # 完成 end_time = datetime.now() duration = end_time - start_time self.log(f"复制完成!") self.log(f"总计: {len(source_files)} 个文件") self.log(f"已复制: {copied} 个文件") self.log(f"跳过: {skipped} 个文件") self.log(f"错误: {errors} 个文件") self.log(f"耗时: {duration.total_seconds():.2f} 秒") self.log(f"完成时间: {end_time.strftime('%H:%M:%S')}") self.log("=" * 60) except Exception as e: self.log(f"严重错误: 复制过程中出现异常 - {str(e)}") def get_source_files(self): """获取源文件列表""" source_files = [] if self.source_type.get() == "local": # 本地目录 source_path = self.source_local_path.get() if not source_path or not os.path.exists(source_path): self.log(f"错误: 源目录不存在或未设置 - {source_path}") return [] self.log(f"扫描本地目录: {source_path}") for root, dirs, files in os.walk(source_path): for file in files: full_path = os.path.join(root, file) rel_path = os.path.relpath(full_path, source_path) source_files.append({ 'type': 'local', 'full_path': full_path, 'rel_path': rel_path }) else: # SMB目录 smb_path = self.source_smb_path.get() if not smb_path: self.log("错误: SMB源目录未设置") return [] server, share, base_path = self.parse_smb_path(smb_path) if not server or not share: self.log(f"错误: SMB路径格式不正确 - {smb_path}") return [] try: self.log(f"连接SMB服务器: {server}/{share}") conn = SMBConnection( self.source_smb_username.get() if self.source_smb_username.get() else '', self.source_smb_password.get() if self.source_smb_password.get() else '', socket.gethostname(), server, domain='', use_ntlm_v2=True ) if not conn.connect(server, 139): self.log(f"错误: 无法连接到SMB服务器 - {server}") return [] # 递归获取文件 self.log(f"扫描SMB目录: {base_path or '/'}") source_files.extend(self.get_smb_files_recursive(conn, share, base_path, '')) conn.close() except Exception as e: self.log(f"错误: 获取SMB文件列表失败 - {str(e)}") return source_files def get_smb_files_recursive(self, conn, share, base_path, rel_path): """递归获取SMB文件列表""" files = [] try: # 构建完整路径 full_path = base_path + '/' + rel_path if rel_path else base_path if not full_path.startswith('/'): full_path = '/' + full_path items = conn.listPath(share, full_path) for item in items: if item.filename in ['.', '..']: continue item_rel_path = os.path.join(rel_path, item.filename) if item.isDirectory: # 递归处理子目录 sub_files = self.get_smb_files_recursive(conn, share, base_path, item_rel_path) files.extend(sub_files) else: # 添加文件 files.append({ 'type': 'smb', 'conn': conn, 'share': share, 'base_path': base_path, 'rel_path': item_rel_path, 'filename': item.filename }) except Exception as e: self.log(f"警告: 无法读取SMB目录内容 - {str(e)}") return files def copy_single_file(self, file_info): """复制单个文件""" # 获取目标路径 target_path_info = self.get_target_path_info(file_info['rel_path']) # 检查目标文件是否存在 if self.copy_mode.get() == "skip" and self.target_file_exists(target_path_info, file_info['rel_path']): return "skipped" try: # 从源读取文件 if file_info['type'] == 'local': # 本地文件 with open(file_info['full_path'], 'rb') as f: file_data = f.read() else: # SMB文件 conn = file_info['conn'] share = file_info['share'] base_path = file_info['base_path'] rel_path = file_info['rel_path'] # 构建完整路径 full_smb_path = base_path + '/' + rel_path if base_path else '/' + rel_path if not full_smb_path.startswith('/'): full_smb_path = '/' + full_smb_path # 使用BytesIO接收文件数据 file_obj = io.BytesIO() try: conn.retrieveFile(share, full_smb_path, file_obj) file_data = file_obj.getvalue() finally: file_obj.close() # 写入目标 if self.target_type.get() == 'local': # 写入本地 target_path = target_path_info os.makedirs(os.path.dirname(target_path), exist_ok=True) with open(target_path, 'wb') as f: f.write(file_data) else: # 写入SMB server, share, base_path = target_path_info conn = SMBConnection( self.target_smb_username.get() if self.target_smb_username.get() else '', self.target_smb_password.get() if self.target_smb_password.get() else '', socket.gethostname(), server, domain='', use_ntlm_v2=True ) if not conn.connect(server, 139): return "error" # 构建完整SMB路径 full_smb_path = base_path + '/' + file_info['rel_path'] if base_path else '/' + file_info['rel_path'] if not full_smb_path.startswith('/'): full_smb_path = '/' + full_smb_path # 创建目录(如果需要) dir_path = os.path.dirname(full_smb_path) if dir_path and dir_path != '/': self.create_smb_directories(conn, share, dir_path) # 写入文件 file_obj = io.BytesIO(file_data) conn.storeFile(share, full_smb_path, file_obj) file_obj.close() conn.close() return "copied" except Exception as e: return "error" def get_target_path_info(self, rel_path): """获取目标路径信息""" if self.target_type.get() == 'local': base_path = self.target_local_path.get() return os.path.join(base_path, rel_path) else: # 对于SMB,返回解析后的信息 smb_path = self.target_smb_path.get() if not smb_path: return None, None, None server, share, base_path = self.parse_smb_path(smb_path) return server, share, base_path def target_file_exists(self, target_path_info, rel_path): """检查目标文件是否存在""" if self.target_type.get() == 'local': target_path = target_path_info return os.path.exists(target_path) else: server, share, base_path = target_path_info if not server or not share: return False try: conn = SMBConnection( self.target_smb_username.get() if self.target_smb_username.get() else '', self.target_smb_password.get() if self.target_smb_password.get() else '', socket.gethostname(), server, domain='', use_ntlm_v2=True ) if not conn.connect(server, 139): return False # 构建完整路径 full_smb_path = base_path + '/' + rel_path if base_path else '/' + rel_path if not full_smb_path.startswith('/'): full_smb_path = '/' + full_smb_path try: attrs = conn.getAttributes(share, full_smb_path) conn.close() return True except: conn.close() return False except: return False def create_smb_directories(self, conn, share, path): """创建SMB目录(递归)""" parts = path.strip('/').split('/') current_path = '' for part in parts: if not part: continue current_path = current_path + '/' + part if current_path else '/' + part try: conn.getAttributes(share, current_path) except: try: conn.createDirectory(share, current_path) except: pass def save_config(self): """保存配置(密码会被加密)""" config = { 'source_type': self.source_type.get(), 'source_local_path': self.source_local_path.get(), 'source_smb_path': self.source_smb_path.get(), 'source_smb_username': self.source_smb_username.get(), 'source_smb_password': self.encrypt_password(self.source_smb_password.get()), 'target_type': self.target_type.get(), 'target_local_path': self.target_local_path.get(), 'target_smb_path': self.target_smb_path.get(), 'target_smb_username': self.target_smb_username.get(), 'target_smb_password': self.encrypt_password(self.target_smb_password.get()), 'copy_mode': self.copy_mode.get(), 'auto_start': self.auto_start.get(), 'background_mode': self.background_mode.get(), 'scheduled_tasks': self.scheduled_tasks, 'config_version': '1.2' # 添加版本号,表示加密配置 } try: with open(self.config_file, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=2) self.log("配置已保存(密码已加密)") messagebox.showinfo("成功", "配置已保存。") except Exception as e: self.log(f"保存配置失败: {str(e)}") messagebox.showerror("错误", f"保存配置失败: {str(e)}") def load_config(self): """加载配置(密码会被解密)""" if not os.path.exists(self.config_file): return try: with open(self.config_file, 'r', encoding='utf-8') as f: config = json.load(f) # 检查配置版本 config_version = config.get('config_version', '1.0') # 加载配置到界面 self.source_type.set(config.get('source_type', 'local')) self.source_local_path.set(config.get('source_local_path', '')) self.source_smb_path.set(config.get('source_smb_path', '')) self.source_smb_username.set(config.get('source_smb_username', '')) # 解密密码 source_password = config.get('source_smb_password', '') if source_password and config_version >= '1.1': source_password = self.decrypt_password(source_password) self.source_smb_password.set(source_password) self.target_type.set(config.get('target_type', 'local')) self.target_local_path.set(config.get('target_local_path', '')) self.target_smb_path.set(config.get('target_smb_path', '')) self.target_smb_username.set(config.get('target_smb_username', '')) # 解密密码 target_password = config.get('target_smb_password', '') if target_password and config_version >= '1.1': target_password = self.decrypt_password(target_password) self.target_smb_password.set(target_password) self.copy_mode.set(config.get('copy_mode', 'overwrite')) # 加载程序设置 self.auto_start.set(config.get('auto_start', False)) self.background_mode.set(config.get('background_mode', False)) # 设置开机自启状态 if self.auto_start.get(): try: self.set_auto_start(True) except: pass # 如果设置失败,忽略 # 如果后台运行模式启用,创建托盘图标 if self.background_mode.get(): self.create_tray_icon() # 加载定时任务 tasks = config.get('scheduled_tasks', []) self.scheduled_tasks = tasks # 更新列表显示 self.task_listbox.delete(0, tk.END) for task in tasks: if task['freq'] == 'weekly': weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] weekday_str = weekdays[task['weekday']] self.task_listbox.insert(tk.END, f"每周{weekday_str} {task['hour']:02d}:{task['minute']:02d}") else: self.task_listbox.insert(tk.END, f"每天 {task['hour']:02d}:{task['minute']:02d}") # 更新UI状态 self.toggle_source_type() self.toggle_target_type() self.log("配置已从文件加载") except Exception as e: self.log(f"加载配置失败: {str(e)}") def log(self, message): """添加日志到日志框""" timestamp = datetime.now().strftime("%H:%M:%S") log_message = f"[{timestamp}] {message}" # 在主线程中更新UI self.root.after(0, self._add_log_message, log_message) def _add_log_message(self, message): """在主线程中添加日志消息""" self.log_text.insert(tk.END, message + "\n") self.log_text.see(tk.END) # 自动滚动到底部 def clear_log(self): """清除日志""" self.log_text.delete(1.0, tk.END) self.log("日志已清除") def start_schedule_thread(self): """启动定时任务线程""" self.running = True self.schedule_thread = threading.Thread(target=self.schedule_worker, daemon=True) self.schedule_thread.start() def schedule_worker(self): """定时任务工作线程 - 修复版""" while self.running: try: if not self.enable_schedule.get() or not self.scheduled_tasks: time.sleep(30) continue current_time = datetime.now() current_date = date.today() current_weekday = current_time.weekday() # 0=周一, 6=周日 current_hour = current_time.hour current_minute = current_time.minute for task in self.scheduled_tasks: # 检查时间是否匹配 if current_hour != task['hour'] or current_minute != task['minute']: continue # 检查今天是否应该执行 should_execute = False if task['freq'] == 'daily': # 每天执行 should_execute = True elif task['freq'] == 'weekly': # 每周特定星期执行 if current_weekday == task['weekday']: should_execute = True if should_execute: # 避免重复执行:检查今天是否已经执行过 execution_key = f"{current_date}_{task['hour']:02d}_{task['minute']:02d}" if task['freq'] == 'weekly': execution_key = f"{current_date}_{task['weekday']}_{task['hour']:02d}_{task['minute']:02d}" if hasattr(self, 'last_execution_day') and self.last_execution_day == execution_key: # 今天已经执行过 continue # 记录执行时间 self.last_execution_day = execution_key # 执行复制任务 self.log(f"定时任务触发: {self.get_task_description(task)}") self.execute_scheduled_copy() # 短暂休眠,避免重复检查 time.sleep(2) # 每分钟检查一次 time.sleep(60 - current_time.second) except Exception as e: self.log(f"定时任务检查出错: {str(e)}") time.sleep(30) def get_task_description(self, task): """获取任务描述""" if task['freq'] == 'weekly': weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] weekday_str = weekdays[task['weekday']] return f"每周{weekday_str} {task['hour']:02d}:{task['minute']:02d}" else: return f"每天 {task['hour']:02d}:{task['minute']:02d}" def execute_scheduled_copy(self): """执行定时复制""" # 在新线程中执行复制,避免界面卡顿 thread = threading.Thread(target=self.scheduled_copy_task, daemon=True) thread.start() def scheduled_copy_task(self): """定时复制任务""" self.copy_files() def on_closing(self): """关闭窗口事件""" if self.background_mode.get() and self.tray_icon: # 如果启用了后台运行且有托盘图标,则最小化到托盘 self.minimize_to_tray() else: # 否则正常退出 self.cleanup() self.root.destroy() def cleanup(self): """清理资源""" self.running = False self.save_config() # 关闭前自动保存配置 if self.tray_icon: self.tray_icon.stop() def main(): root = tk.Tk() app = SMBCopierApp(root) root.mainloop() if __name__ == "__main__": # 检查依赖库 try: import tkinter from tkinter import scrolledtext from smb.SMBConnection import SMBConnection import schedule from cryptography.fernet import Fernet import pystray from PIL import Image, ImageDraw except ImportError as e: print(f"缺少依赖库: {e}") print("请安装以下库:") print("pip install pysmb schedule cryptography pystray Pillow") sys.exit(1) main()
2025年12月18日
27 阅读
0 评论
0 点赞
2025-12-16
anolis8 安装cockpit管理平台 进行KVM虚拟机 podman容器管理
安装cockpit和kvm、podman管理插件yum install cockpit systemctl enable cockpit systemctl start cockpit yum install -y qemu-kvm libvirt virt-manager systemctl enable --now libvirtd yum install -y cockpit-machines yum install virt-viewer sudo firewall-cmd --permanent --zone=public --add-service=cockpit sudo firewall-cmd --reload yum install epel-release -y dnf install cockpit-podman -y使用系统用户登录控制台https://IP:9090/
2025年12月16日
25 阅读
0 评论
0 点赞
2025-12-16
彻底清理window10、windows11、Edge浏览器微软用户邮箱绑定
参考链接https://www.zhihu.com/question/8239136687https://www.azurew.com/%e9%94%99%e8%af%af%e8%a7%a3%e5%86%b3/13462.html清除与administrator绑定的微软账号作者:兔吮毫链接:https://www.zhihu.com/question/8239136687/answer/67329291890来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。步骤一:打开注册表,快捷键Win+R 输入 regedit ,点确定打开注册表步骤二:按路径找到地址(可以复制到地址栏一键直达),将这里的邮箱文件夹右键删除计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\IdentityCRL\UserExtendedProperties计算机\HKEY_USERS.DEFAULT\Software\Microsoft\IdentityCRL\StoredIdentities步骤三:删除后注销电脑,重新登陆,继续下一步。步骤四:Win+R 输入regedit,打开注册表,找到地址,将 IdentityCRL 文件夹右键删除计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\IdentityCRL计算机\HKEY_USERS.DEFAULT\Software\Microsoft\IdentityCRL步骤五:注销电脑,重新登陆,进入设置账户,你可以看到删除出现了,它终于出现了至此,Administrator与微软的关系解绑了。步骤六:删除登录密码,登录选项-密码-更改-输入当前密码,新密码不写直接点完成清除Edge浏览器微软用户绑定当前用户下的edge浏览器用户数据。如我的当前用户为"administrator",位置应该是:"C:\Users\Administrator\AppData\Local\Microsoft\Edge"删除Edge目录和Edgexx开头的目录
2025年12月16日
96 阅读
0 评论
0 点赞
2025-12-08
PostgreSQL启用密码 网段白名单 配置文件热加载 pg_ctl reload
部署postgresql数据库yum install postgresql-server查看数据库版本psql --version psql (PostgreSQL) 13.14初始化数据库postgresql-setup initdb开启数据库密码验证,取消postgresql.conf配置文件中的注释/var/lib/pgsql/data/postgresql.conf password_encryption = md5 # md5 or scram-sha-256如果数据库不在本地,要通过网络访问,则需修改监听地址和防火墙开放对应端口/var/lib/pgsql/data/postgresql.conf listen_addresses = '*' # what IP address(es) to listen on; port = 5432配置数据库访问策略,允许本地网络使用密码访问数据库/var/lib/pgsql/data/pg_hba.conf # TYPE DATABASE USER ADDRESS METHOD # IPv4 local connections: host all all 127.0.0.1/32 md5 # IPv6 local connections: host all all ::1/128 md5 如果要允许所有外部IP访问,地址要写all host all all all md5启动数据库systemctl start postgresql systemctl enable postgresql配置文件热加载 pg_ctl reload,热加载不终端服务器root# su - postgres postgres$ pg_ctl reload -D /var/lib/pgsql/data 提示server sianaled代表成功
2025年12月08日
27 阅读
0 评论
0 点赞
2025-12-04
深信服下一代防火墙、深信服全网行为管理设备密码重置 U盘 交叉线
1. U盘恢复密码从标准版本12.0.42开始,AC/SG设备支持通过U盘恢复密码。具体步骤如下:新建一个txt文档,重命名为reset-cfg.txt,并将其拷贝到U盘根目录,U盘格式需为FAT32。确保电脑和设备可以通信,访问设备地址 https://ACIP/php/rp.php,网页会提示“创建文件成功,请连接交叉线并重启设备”。请确保有“创建文件成功”的提示。将U盘插入设备,拔掉所有网口网线(记住接线信息),然后重启设备。等待大约10分钟后,拔出U盘,尝试使用默认帐号admin和密码admin登录设备。注意:U盘恢复密码完成后,建议手动重启一次设备[1]。2. 交叉线恢复密码如果您的设备版本为AC/SG5.5R1及以上,可以使用交叉线恢复密码。步骤如下:确保电脑和设备可以通信,访问设备地址 https://ACIP/php/rp.php,确保有“创建文件成功”的提示。准备交叉线短接设备任意两个非bypass电口,拔掉其他网口网线,只保留短接的交叉线。手动重启设备,等待大约10分钟后,拔掉交叉线,尝试使用默认帐号admin和密码admin登录设备[1]。3. 通过备份配置或管理员恢复密码如果您有备份的配置,或者有administrator角色的二级管理员,可以提供加盖公司公章的密码重置申请函,协调工程师协助恢复密码。如果没有备份配置且需要配置,建议使用上面提到的U盘或交叉线方式恢复密码实测方法二无法重置,使用方法一重置成功
2025年12月04日
49 阅读
0 评论
1 点赞
2025-11-26
VeeamBackup&Replication12高级技巧 NAS存储 任务异地备份 Backup Copy Job
1、添加仓库Backup Infrastructure - Backup Repositories - Add Repository可添加的类型有Direct attatched storage 直连存储Network attached storage 网络存储Deduplicating storage appliance 去重存储object storage 对象存储veeam data cloud vault veeam数据云选择网络存储可添加NFS SMB可添加群晖SMB2、创建Backup Copy Job任务Home - Backup Copy可选Image-level backup、Application-level backupImmediate copy 为备份后立即复制Periodic copy 为定期复制备份目标可以为jobs任务或repositories仓库可配置存储位置和策略配置完成
2025年11月26日
36 阅读
0 评论
0 点赞
2025-11-20
VeeamBackup&Replication12高级技巧使用veeam-recovery异机恢复SUSE Linux Enterprise Server 15 SLES15
一、问题描述某物理数据库服务器,使用SUSE Linux Enterprise Server 15 (SLES15)系统,具备系统盘和FC存储,数据库运行于FC存储中;服务器整机通过Veeam备份,还原到虚拟机中进行测试时,由于磁盘路径变动,系统无法启动,经过多轮测试后成功启动系统本文记录异机恢复后的系统修改过程二、工具准备与流程Veeam备份服务器信息Veeam恢复镜像 veeam-recovery-amd64-6.0.0.iso下载地址 https://repository.veeam.com/backup/linux/agent/veeam-recovery-media/x64/SLES15安装镜像,具备救援模式 SLE-15-SP3-Full-x86_64-GM-Media1.iso整体流程配置虚拟机-使用Veeam恢复镜像还原系统,正确映射分区-启动SLES15救援模式,识别lvm,修改fstab,修改网络配置-启动系统-测试业务三、恢复系统1、创建虚拟机2、挂载并进入veeam-recovery恢复模式3、配置网络配置网络调用的nmtui,配置为能够和veeam通信的IP即可4、进行恢复从Veeam服务器恢复登陆服务器,选择要恢复的备份5、映射磁盘务必注意,先将5T磁盘映射为原447.1G系统盘,free剩余空间映射1.99T的LVM卷此时lvm卷会显示为sda4(lvm),并下方显示VG和LV卷为/hana分区6、进行恢复按S 启动恢复即可 Start restore数据量越大恢复越慢四、启动救援模式恢复完成启动系统后,会卡在启动项无法进入系统挂载SLE-15-SP3-Full-x86_64-GM-Media1.iso,进入恢复模式挂载系统进行修复查看当前磁盘情况fdisk -l挂载磁盘 mount /dev/sda3 /mnt/ mount /dev/sda1 /mnt/boot/efi mount -o bind /dev /mnt/dev mount -o bind /proc /mnt/proc mount -o bind /sys /mnt/sys 进入chroot模式 chroot /mnt 挂载lvm卷 扫描pv pvscan 扫描vg vgscan 激活vg卷 vgchange -ay VG 扫描逻辑卷 lvscan 修复fstab为当前实际盘符 vi /etc/fstab 禁用多路径服务 systemctl disable multipathd 重建initramfs mkinitrd -f 配置网络 yast2 选择System-Network Settings 删除旧网卡,按Overview-DNS-Routing分别配置 注意:yast2很不好用是正常的,他不如nmtui操作简单 重启五、连接测试启动系统桌面黑屏,但ssh连接正常数据库工具连接数据库测试正常恢复试验成功六、一点小问题虽然SSH和业务系统都已经正常了,但是进不去桌面,还需要进一步排查原因桌面服务不影响系统和业务运行七、桌面显示问题解决了AI分析解决的,是因为原机器是第三代志强可扩展,测试虚拟机是第一代志强可扩展,导致的软件不能启动,可以临时修改为softpipe启动。根据提供的日志,我们可以看到gnome-shell不断崩溃并重启。日志中关键的错误信息包括: "traps: llvmpipe-0[3913] trap invalid opcode ip:7f296830a80c sp:7f29deaff560 error:0" "gnome-shell-x11.service: Main process exited, code=killed, status=4/ILL" "JS WARNING: [resource:///org/gnome/shell/ui/layout.js 24]: reference to undefined property "MetaWindowX11"" 其中,invalid opcode(非法指令)和ILL(非法指令信号)表明gnome-shell在尝试执行一个当前CPU不支持的指令。这通常是由于CPU微架构不兼容导致的,比如在迁移后新服务器的CPU与旧服务器不同,可能新CPU较老,不支持某些指令集(如SSE4.2、AVX等),而gnome-shell或其中的组件(如llvmpipe)是使用这些指令集编译的。 另外,llvmpipe是Mesa软件渲染器,它使用LLVM进行即时编译,可能会生成针对特定CPU的优化代码。如果迁移后的服务器CPU不支持这些指令,就会导致非法指令错误。 我们按照以下步骤操作: 通过SSH连接到服务器。 切换到root用户。 编辑/etc/environment,添加一行: GALLIUM_DRIVER=softpipe 重启系统。 如果重启后图形界面能够正常启动,那么问题就解决了。
2025年11月20日
65 阅读
0 评论
0 点赞
2025-10-22
docker、K8S Kubernetes集群部署学习笔记
docker学习清华镜像源 yum install -y yum-utils sudo yum-config-manager --add-repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo sudo sed -i 's|https://download.docker.com|https://mirrors.tuna.tsinghua.edu.cn/docker-ce|g' /etc/yum.repos.d/docker-ce.repo 安装docker yum install docker-ce docker-ce-cli containerd.io systemctl enable --now docker 安装命令补全 yum install bash-completion -y 执行容器中的终端 docker exec -it 名称 bash docker run -d centos docker run -it -d nginx docker ps -a 查看所有容器,包含退出 -q 列出所有的容器ID docker rm -f 删除容器 映射端口、目录 docker run -d --name web -p 88:80 -v /opt/wwwroot/:/usr/share/nginx/html nginxFROM 构建新镜像是基于哪个镜像LABEL 标签RUN 构建镜像时运行的Shell命令COPY 拷贝文件或目录到镜像中ADD 解压压缩包并拷贝ENV 设置环境变量USER 为RUN、CMD和ENTRYPOINT执行命令指定运行用户EXPOSE 声明容器运行的服务端口WORKDIR 为RUN、CMD、ENTRYPOINT、COPY和ADD设置工作目录CMD 运行容器时默认执行,如果有多个CMD指令,最后一个生效docker编译docker build -t nginx:v1 .nginx镜像FROM centos:7 RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \ yum install epel-release -y && \ yum install nginx -y CMD ["nginx","-g","daemon off;"]centos底包FROM centos:7 FROM centos:8 FROM almalinux:latest FROM almalinux/8-minimaltomcat镜像手动docker run -it -d alma:tnt docker exec -it 名称 bash dnf install java-openjdk -y dnf clean all rm -rf /var/cache/dnf/* curl -O https://mirrors.huaweicloud.com/apache/tomcat/tomcat-10/v10.1.33/bin/apache-tomcat-10.1.33.tar.gz tar xvzf apache-tomcat-10.1.33.tar.gz export TOMCAT_HOME=/apache-tomcat-10.1.33/ /apache-tomcat-10.1.33/bin/catalina.sh run创建dockerfileFROM almalinux:latest RUN dnf install java-openjdk -y &&\ dnf install java-openjdk -y &&\ dnf clean all &&\ rm -rf /var/cache/dnf/* RUN curl -O https://mirrors.huaweicloud.com/apache/tomcat/tomcat-10/v10.1.33/bin/apache-tomcat-10.1.33.tar.gz &&\ tar xvzf apache-tomcat-10.1.33.tar.gz &&\ export TOMCAT_HOME=/apache-tomcat-10.1.33/ &&\ rm -rf /apache-tomcat-10.1.33.tar.gz EXPOSE 8080 CMD ["/apache-tomcat-10.1.33/bin/catalina.sh","run"]编译运行docker build -t tomcat:tnt . docker run -d --name tomcattnt -p 8080:8080 tomcat:tntteleport堡垒机镜像手动docker run -it -d centos:7 docker exec -it 名称 bash curl -O https://tp4a.com/static/download/teleport-server-linux-x64-3.6.4-b3.tar.gz tar -zxvf teleport-server-linux-x64-3.6.4-b3.tar.gz rm -rf /teleport-server-linux-x64-3.6.4-b3.tar.gz mkdir /usr/local/teleport/data/assist -p cd /usr/local/teleport/data/assist curl -O https://tp4a.com/static/download/teleport-assist-windows-3.6.3.exe curl -O https://tp4a.com/static/download/teleport-assist-macos-3.6.3.dmg cd teleport-server-linux-x64-3.6.4-b3 sh -c '/bin/echo -e "\n" | sh ./setup.sh' sed -i 's/exit $shell_ret/\/usr\/bin\/tail -f \/usr\/local\/teleport\/data\/log\/tpcore.log/g' /usr/local/teleport/start.shdockerfile文件 teleportV3FROM centos:7 RUN curl -O https://tp4a.com/static/download/teleport-server-linux-x64-3.6.4-b3.tar.gz &&\ tar -zxvf teleport-server-linux-x64-3.6.4-b3.tar.gz &&\ rm -rf /teleport-server-linux-x64-3.6.4-b3.tar.gz &&\ mkdir /usr/local/teleport/data/assist -p &&\ cd /usr/local/teleport/data/assist &&\ curl -O https://tp4a.com/static/download/teleport-assist-windows-3.6.3.exe &&\ curl -O https://tp4a.com/static/download/teleport-assist-macos-3.6.3.dmg &&\ cd /teleport-server-linux-x64-3.6.4-b3 &&\ sh -c '/bin/echo -e "\n" | sh ./setup.sh' &&\ sed -i 's/exit $shell_ret/\/usr\/bin\/tail -f \/usr\/local\/teleport\/data\/log\/tpcore.log/g' /usr/local/teleport/start.sh &&\ rm -rf /teleport-server-linux-x64-3.6.4-b3 EXPOSE 7190 52089 52189 52389 CMD ["/etc/init.d/teleport","start"]编译运行docker build -t teleportv3:tnt . docker run -d --name teleportv3 -p 7190:7190 -p 52089:52089 -p 52189:52189 -p 52389:52389 teleportv3:tnt安装harbor下载解压harbor-online-installer-v2.11.2.tgz进入目录 cd /harbor 创建配置 cp harbor.yml.tmpl harbor.yml hostname: http: port:预配置./prepare部署./install.sh查看状态docker-compose ps关闭docker-compose stop启动docker-compose start后台运行docker-compose up -d删除harbor docker-compose down默认账号密码admin Harbor12345docker主机添加Harbor仓库{ "registry-mirrors": [ "https://docker.1panel.live" ], "insecure-registries" : ["10.100.0.1"] }查看docker配置是否成功docker info登录harbordocker login IP修改镜像tagdocker tag teleportv3:tnt 10.100.0.2/library/teleportv3:tnt推送docker push 10.100.0.2/library/teleportv3:tnt拉取镜像10.100.0.2/library/teleportv3:tnt{lamp/}K8S快速部署1、主机规划testk8s-master 192.168.4.10 testk8s-node1 192.168.4.11 testk8s-node2 192.168.4.12系统配置为4C8G200G,centos7系统,分区为/boot、/,无SWAP分区2、操作系统初始化-所有节点关闭防火墙systemctl stop firewalld systemctl disable firewalld关闭selinuxsed -i 's/enforcing/disabled/' /etc/selinux/config setenforce 0关闭swapswapoff -a # 临时 sed -ri 's/.*swap.*/#&/' /etc/fstab # 永久在master节点添加hostscat >> /etc/hosts << EOF 192.168.4.10 testk8s-master 192.168.4.11 testk8s-node1 192.168.4.12 testk8s-node2 EOF将桥接的IPv4流量传递到iptables的链cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system # 生效时间同步vi /etc/chrony.conf 增加 server 114.115.116.117 iburst systemctl restart chronyd 立即同步时间 chronyc -a makestep 查看同步状态 chronyc tracking 3、安装docker配置阿里云、清华镜像源curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo yum install -y yum-utils sudo yum-config-manager --add-repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo sudo sed -i 's|https://download.docker.com|https://mirrors.tuna.tsinghua.edu.cn/docker-ce|g' /etc/yum.repos.d/docker-ce.repo yum clean all yum makecache yum install bash-completion -y yum install docker-ce -y --nogpgcheck systemctl enable docker && systemctl start docker systemctl restart docker docker info4、安装vmtoolsyum install open-vm-tools -y5、做快照防止操作错误6、安装kubeadm,kubelet和kubectl配置镜像加速镜像源列表https://www.cnblogs.com/gnuorg/p/18570325cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://docker.1panel.live"] } EOF systemctl restart docker cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF 指定版本号yum install -y kubelet-1.20.0 kubeadm-1.20.0 kubectl-1.20.0 systemctl enable kubelet7、部署Kubernetes Master在192.168.4.10(Master)执行。kubeadm init \ --apiserver-advertise-address=192.168.4.10 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.20.0 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16 \ --ignore-preflight-errors=all 解释 --apiserver-advertise-address 集群通告地址 --image-repository 由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址 --kubernetes-version K8s版本,与上面安装的一致 --service-cidr 集群内部虚拟网络,Pod统一访问入口 --pod-network-cidr Pod网络,与下面部署的CNI网络组件yaml中保持一致 --ignore-preflight-errors=all 忽略错误 初始化完成后,最后会输出一个join命令,先记住,下面用。执行后返回Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.4.10:6443 --token oweerb.nonsh3zl5a8no0od \ --discovery-token-ca-cert-hash sha256:279352b82d65dd6bd470ea1b8c54542215696402a0d6bd8a20e53102f39f8a21 拷贝kubectl使用的连接k8s认证文件到默认路径 mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config查看工作节点kubectl get nodes NAME STATUS ROLES AGE VERSION testk8s-master NotReady control-plane,master 104s v1.20.08、加入K8S node在Node节点执行192.168.4.11192.168.4.12向集群添加新节点,执行在kubeadm init输出的kubeadm join命令kubeadm join 192.168.4.10:6443 --token oweerb.nonsh3zl5a8no0od \ --discovery-token-ca-cert-hash sha256:279352b82d65dd6bd470ea1b8c54542215696402a0d6bd8a20e53102f39f8a21默认token有效期为24小时,当过期之后,该token就不可用了。这时就需要重新创建token,可以直接使用命令快捷生成kubeadm token create --print-join-command查看工作节点kubectl get nodes NAME STATUS ROLES AGE VERSION testk8s-master NotReady control-plane,master 3m42s v1.20.0 testk8s-node1 NotReady <none> 19s v1.20.0 testk8s-node2 NotReady <none> 16s v1.20.09、部署容器网络(CNI)Calico是一个纯三层的数据中心网络方案,是目前Kubernetes主流的网络方案。下载YAMLcurl https://docs.projectcalico.org/v3.20/manifests/calico.yaml -O下载完后还需要修改里面定义Pod网络(CALICO_IPV4POOL_CIDR),与前面kubeadm init的 --pod-network-cidr指定的一样。# The default IPv4 pool to create on startup if none exists. Pod IPs will be # chosen from this range. Changing this value after installation will have # no effect. This should fall within `--cluster-cidr`. - name: CALICO_IPV4POOL_CIDR value: "10.244.0.0/16"修改完后文件后,部署:kubectl apply -f calico.yaml kubectl get pods -n kube-system等Calico Pod都Running,节点也会准备就绪。注:以后所有yaml文件都只在Master节点执行!安装目录:/etc/kubernetes/组件配置文件目录:/etc/kubernetes/manifests/节点运行情况 kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE calico-kube-controllers-577f77cb5c-jrcfs 0/1 Pending 0 5s calico-node-fznrr 0/1 Init:0/3 0 6s calico-node-nrrwj 0/1 Init:0/3 0 6s calico-node-x7hds 0/1 Init:0/3 0 6s coredns-7f89b7bc75-6lr2s 0/1 Pending 0 7m18s coredns-7f89b7bc75-kwq9c 0/1 Pending 0 7m18s etcd-testk8s-master 1/1 Running 0 7m26s kube-apiserver-testk8s-master 1/1 Running 0 7m26s kube-controller-manager-testk8s-master 1/1 Running 0 7m26s kube-proxy-6pbwh 1/1 Running 0 4m9s kube-proxy-btgsz 1/1 Running 0 4m12s kube-proxy-cdfxc 1/1 Running 0 7m18s kube-scheduler-testk8s-master 1/1 Running 0 7m26s 会出现的一种情况是镜像下载失败calico-node-fznrr 0/1 Init:ImagePullBackOff 0 5m32s calico-node-nrrwj 0/1 Init:ImagePullBackOff 0 5m32s calico-node-x7hds 0/1 Init:ImagePullBackOff 0 5m32s 查看失败原因 kubectl describe po calico-node-fznrr -n kube-system Warning Failed 2m11s kubelet Failed to pull image "docker.io/calico/pod2daemon-flexvol:v3.20.6": rpc error: code = Unknown desc = Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) Warning Failed 2m11s kubelet Error: ErrImagePull Normal BackOff 2m11s kubelet Back-off pulling image "docker.io/calico/pod2daemon-flexvol:v3.20.6" Warning Failed 2m11s kubelet Error: ImagePullBackOff Normal Pulling 116s (x2 over 5m31s) kubelet Pulling image "docker.io/calico/pod2daemon-flexvol:v3.20.6" 通过镜像站点下载 https://docker.aityp.com/image/docker.io/calico/pod2daemon-flexvol:v3.20.6docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/calico/pod2daemon-flexvol:v3.20.6 docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/calico/pod2daemon-flexvol:v3.20.6 docker.io/calico/pod2daemon-flexvol:v3.20.6 等待自动修复完成calico-kube-controllers-577f77cb5c-jrcfs 0/1 ContainerCreating 0 22m calico-node-fznrr 0/1 Running 0 22m calico-node-nrrwj 1/1 Running 0 22m calico-node-x7hds 1/1 Running 0 22m 有时发生错误,重启k8s也能解决systemctl restart kubelet创建pod测试kubectl create deployment nginx --image=nginx查看pod状态kubectl get pod查看pod状态带节点和IPkubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-6799fc88d8-rqb82 1/1 Running 0 14m 10.244.236.3 testk8s-node1 <none> <none> 测试nginxcurl 10.244.236.3 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> 创建外部访问kubectl expose deployment nginx --port=80 --target-port=80 --type=NodePort查看外部端口 范围 30000以上kubectl get pod,svc NAME READY STATUS RESTARTS AGE pod/nginx-6799fc88d8-rqb82 1/1 Running 0 15m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46m service/nginx NodePort 10.101.228.228 <none> 80:32507/TCP 15s访问地址为http://192.168.4.11:32507/ http://192.168.4.12:32507/即Pod任意节点IP,组合service映射的端口10、部署dashboardYAML下载地址curl https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.3/aio/deploy/recommended.yaml -O修改yaml,增加nodePort: 30001 type: NodePortkind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: NodePort ports: - port: 443 targetPort: 8443 nodePort: 30001 selector: k8s-app: kubernetes-dashboard部署dashboardkubectl apply -f recommended.yaml查看状态kubectl get pods -n kubernetes-dashboard创建service account并绑定默认cluster-admin管理员集群角色:创建用户kubectl create serviceaccount dashboard-admin -n kube-system用户授权kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin获取用户Tokenkubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/ {print $1}') Name: dashboard-admin-token-sqtsm Namespace: kube-system Labels: <none> Annotations: kubernetes.io/service-account.name: dashboard-admin kubernetes.io/service-account.uid: 77ad4c5d-e4e0-4dc9-b014-7f679acf5aff Type: kubernetes.io/service-account-token Data ==== ca.crt: 1066 bytes namespace: 11 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6InY0U0pqNDh2M0ZGMVdMTGdxSnNBcmxMaVFGVE9nMC1tMnhxQzFfZjF3aEUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tc3F0c20iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzdhZDRjNWQtZTRlMC00ZGM5LWIwMTQtN2Y2NzlhY2Y1YWZmIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.fwivtHsitw0ABTfb96HqIJ6N9SL23eiZtIjniqB1qRYIODkGJkOXKGpUmEXPRwR-pQr4glk1KDP9dB2xidET9IhZ-3iKt_5K8xb9K3aELG9yOzzH0Xmi88SaY6A6ZrABaCjjTcp80d-5FgQhRB6ruMLnD1N7vftYk1Sf37HvZ_bKApq1C6uebKnMd0M2EcPckjepvSXmD6fdsosTAJrTYeEpcFCjR6IS5R9bnrN7ADwFZHu-kEekhhV7g888REdhnbSkAvzE9OYbIf7uVgTkh6C_ZhJEzODViHS_RDkiEbZSqs0Q53h50CgL8tj3CBrkV9FvO7SoKVCtvTkYZyPfcQ 访问地址:https://NodeIP:30001任何节点都可以访问https://192.168.4.10:30001/ https://192.168.4.11:30001/EDGE访问出现你的连接不是专用链接,没有继续访问按钮时解决办法保持焦点在页面内,鼠标在页面空白处点击(不选中任何按钮),直接输入“thisisunsafe”,输完后按回车键,就可以正常访问网页。这里要注意的是,输入的时候页面时不会有任何反应的,也不会显示输入的字符,是正常现象。输入完毕后点回车即可。输入Token登录token: eyJhbGciOiJSUzI1NiIsImtpZCI6InY0U0pqNDh2M0ZGMVdMTGdxSnNBcmxMaVFGVE9nMC1tMnhxQzFfZjF3aEUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tc3F0c20iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzdhZDRjNWQtZTRlMC00ZGM5LWIwMTQtN2Y2NzlhY2Y1YWZmIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.fwivtHsitw0ABTfb96HqIJ6N9SL23eiZtIjniqB1qRYIODkGJkOXKGpUmEXPRwR-pQr4glk1KDP9dB2xidET9IhZ-3iKt_5K8xb9K3aELG9yOzzH0Xmi88SaY6A6ZrABaCjjTcp80d-5FgQhRB6ruMLnD1N7vftYk1Sf37HvZ_bKApq1C6uebKnMd0M2EcPckjepvSXmD6fdsosTAJrTYeEpcFCjR6IS5R9bnrN7ADwFZHu-kEekhhV7g888REdhnbSkAvzE9OYbIf7uVgTkh6C_ZhJEzODViHS_RDkiEbZSqs0Q53h50CgL8tj3CBrkV9FvO7SoKVCtvTkYZyPfcQ11、查看日志查看容器日志kubectl logs 容器名称 -n kube-system kubectl get pod NAME READY STATUS RESTARTS AGE nginx-6799fc88d8-rqb82 1/1 Running 0 37m kubectl logs nginx-6799fc88d8-rqb82 /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh查看容器事件kubectl describe pod 容器名称 -n kube-system kubectl describe pod nginx-6799fc88d8-rqb82 Name: nginx-6799fc88d8-rqb82 Namespace: default Priority: 0 Node: testk8s-node1/192.168.4.11 Start Time: Wed, 22 Oct 2025 14:17:29 +0800 Labels: app=nginx pod-template-hash=6799fc88d8查看calico.yaml所需要的镜像 grep image calico.yaml image: docker.io/calico/cni:v3.20.6 image: docker.io/calico/cni:v3.20.6 image: docker.io/calico/pod2daemon-flexvol:v3.20.6 image: docker.io/calico/node:v3.20.6 image: docker.io/calico/kube-controllers:v3.20.6 cailco镜像下载失败时解决办法通过镜像站下载 https://docker.aityp.com/清空部署环境kubeadm reset系统命令补全 yum install bash-completion -y{lamp/}扩展学习docker切换containerd1、先决条件节点执行 cat <<EOF | tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter 设置内核参数 cat <<EOF | tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF 生效内核参数 sysctl --system2、安装containerd 节点执行安装依赖包yum install -y yum-utils device-mapper-persistent-data lvm2安装docker仓库更新系统、安装containerdyum update -y yum install -y containerd.io配置containerd创建配置目录 mkdir -p /etc/containerd 创建默认配置文件 containerd config default | sudo tee /etc/containerd/config.toml重启containerdsystemctl restart containerd修改配置文件vi /etc/containerd/config.toml sandbox_image = "registry.k8s.io/pause:3.6" 修改为 registry.aliyuncs.com/google_containers/pause:3.2 SystemdCgroup = false 修改为 SystemdCgroup = true [plugins."io.containerd.grpc.v1.cri".registry.mirrors] 下增加两行 [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://docker.xuanyuan.me/"] 对接kubeletvi /etc/sysconfig/kubelet KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd停止dockersystemctl stop docker.socket&&systemctl stop docker && systemctl disable docker3、替换容器引擎master查看K8S状态kubectl get node NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane,master 25h v1.20.0 k8s-node1 NotReady <none> 25h v1.20.0 k8s-node2 Ready <none> 25h v1.20.0节点重启kubeletsystemctl restart kubelet稍后master再次查看K8S状态kubectl get node NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane,master 25h v1.20.0 k8s-node1 Ready <none> 25h v1.20.0 k8s-node2 Ready <none> 25h v1.20.0mater查看详细节点信息kubectl get node -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-master Ready control-plane,master 25h v1.20.0 192.168.4.10 <none> CentOS Linux 7 (Core) 3.10.0-1160.el7.x86_64 docker://26.1.4 k8s-node1 Ready <none> 25h v1.20.0 192.168.4.11 <none> CentOS Linux 7 (Core) 3.10.0-1160.el7.x86_64 containerd://1.6.33 k8s-node2 Ready <none> 25h v1.20.0 192.168.4.12 <none> CentOS Linux 7 (Core) 3.10.0-1160.el7.x86_64 docker://26.1.4更多K8S命令查看master组件状态kubectl get cs查看k8s所有资源kubectl api-resources查看node状态kubectl get node 查看APIserver代理的URLkubectl cluster-info查看集群详细信息kubectl cluster-info dump查看资源信息kubectl describe pod 名称 查看事件 kubectl get pods --watch 实时查看pod
2025年10月22日
324 阅读
0 评论
2 点赞
2025-10-21
使用RROrg在物理机虚拟机上快速部署黑群晖NAS系统 DiskStation Manager DSM7
本文旨在介绍通过rr在虚拟机上快速部署黑群晖系统,为是否购买群晖NAS做出决策,如安装后无法适用群晖系统的操作,或感觉界面复古、操作繁琐,仅需一台家庭个人NAS,可安装飞牛OS进行尝试rr网站 https://rrorg.cn/rr项目地址 https://github.com/RROrg/rr官方使用说明 https://rrorg.cn/archives/Use%20guide1、下载rr我使用vmware虚拟机部署,因此下载了rr-25.9.7.ova.zip,下载并解压出rr.ova2、创建虚拟机部署OVF模板选择本地文件rr.ova指定名称选择主机、分配网络、等待上传部署完成;其中网络要支持DHCP,能够自动分配IP地址;磁盘可根据情况选择精简置备或厚置备3、修改硬盘容量默认为2核4G 3.49G系统盘 32G数据盘调整CPU内存如4核8G,将32G数据盘改为需求大小,如2T,保存并启动4、安装系统系统自动启动,直至进入rr页面登录rr地址http://192.168.4.200:7681/修改语言setting menu - choose a language - zh-CN选择型号 如DS920+选择版本 7.2,下载pat版本到电脑编译引导启动等待几分钟,从浏览器访问http://192.168.4.200:5000/安装系统等待安装完自动重启5、初始化空间访问http://192.168.4.200:5000/6、完成愉快的使用群晖吧
2025年10月21日
69 阅读
0 评论
0 点赞
2025-10-21
SAP HANA2.0数据库导入导出、异机恢复、租户覆盖租户
SAP HANA2.0是SAP公司的一款内存数据库,适配SAP公司的ERP等系统,异机恢复通常用于,开发环境部署调试完成后,覆盖到测试、正式环境。1、备份租户数据库使用SAP HANA Studio登录要备份的数据库SYSTEMDB租户在数据库上右键备份与恢复 Backup and Recovery备份租户数据库 Back Up Tenant Database..选择要备份的租户完整备份 Complete Data Backup备份类型为文件 File备份路径自定义,如 /tmp/hanabak ,目录需要写入权限,注意空间是否充足备份文件名自定义,如 xxx202510进行备份即可2、传送至目标服务器在备份主机上将文件传送至远程主机scp -r -P 22 /tmp/hanabak user@remote_host:/home/user/3、恢复租户数据库使用SAP HANA Studio登录要恢复的数据库SYSTEMDB租户在数据库上右键备份与恢复 Backup and Recovery备份租户数据库 Recover Tenant Database..选择要恢复的租户恢复到具体的备份 Recover the database to a specific data backup从目录搜索备份,注意是图片中未选中的那项Search for the backup catalog in the file system only输入scp传送过来的目录恢复完成即可
2025年10月21日
48 阅读
0 评论
0 点赞
2025-09-11
红帽RHCSA RHCE RHCA 华为欧拉HCIA HCIP HCIE升级认证 超低价
https://www.yutianedu.com/YTZX471/info.aspx?itemid=2418目前誉天有这个活动,在2025年12月底前,持有红帽RHCSA RHCE RHCA认证的工程师,可以超低价升级考试华为认证详情在誉天官网找老师咨询https://www.yutianedu.com/
2025年09月11日
105 阅读
0 评论
0 点赞
2025-09-11
腾讯免费安全CDN边缘安全加速平台 EO (TencentCloud EdgeOne)
本站已接入边缘安全加速平台 EO想免费使用腾讯edge one,分为两种情况,备案域名和未备案域名,未备案域名仅能使用海外节点。一、未备案域名https://edgeone.ai/zh/get-free-plan访问edgeone国际官网,通过测速和看教程,能获得腾讯国际版4个免费套餐,注意,不包含中国大陆二、备案域名https://cloud.tencent.com/act/pro/eo-freeplan?from=28412访问edgeone国内版活动页面,通过扫码抽奖的方式领取兑换码同时也有其他的获取方式三、源站防护1、DNS解析清理90APT仅有两条记录,不包含真实IP2、源站访问白名单在服务器防火墙中配置http、https协议的访问IP为白名单IP,防止对源服务器进行探测四、小结强大!免费!YYDS!
2025年09月11日
177 阅读
0 评论
0 点赞
1
2
...
23