作者: 启鑫

  • OLLAMA 安装与配置指南

    OLLAMA 安装与配置指南

    1. 什么是 LLM(大语言模型)

    大语言模型(LLM,Large Language Model)是一类基于深度学习的人工智能模型,专门用于自然语言处理(NLP)任务。这些模型通过大规模数据训练,具备强大的文本理解、生成和推理能力。

    LLM 具有以下特点:

    • 大规模参数:通常拥有数十亿甚至上千亿的参数,使其能够处理复杂的语言任务。
    • 多任务适应性:可以用于文本生成、翻译、问答、代码补全等任务。
    • 上下文理解能力:可以根据上下文生成连贯且符合语境的文本。
    • 自我学习能力:能够通过微调适应特定领域的应用。

    常见的大语言模型包括 OpenAI 的 GPT 系列、Meta 的 LLaMA 系列以及其他主流开源模型。

    2. 为什么使用 OLLAMA

    OLLAMA 是一个强大的 AI 模型管理和运行工具,提供了高效、便捷的 LLM 部署方式,主要优势包括:

    • 简易部署:支持快速安装和配置,适合开发者和研究人员。
    • 高效运行:优化的推理性能,减少计算资源占用,提高响应速度。
    • 灵活扩展:支持多个模型的下载和运行,满足不同任务需求。
    • 跨平台支持:兼容 Windows、Linux 和 macOS,适用于不同开发环境。

    3. 安装 OLLAMA

    可以通过以下链接下载并安装 OLLAMA:
    OLLAMA 官方网站

    4. 下载模型

    安装完成后,可以从 OLLAMA 官方库中下载所需的 AI 模型:
    OLLAMA 模型库

    使用以下命令运行指定的模型,例如 llama3.3 70B 版本:

    ollama run llama3.3:70b
    

    5. Windows 环境变量配置

    在 Windows 系统中,可以通过环境变量配置 OLLAMA 运行环境,设置如下:

    # 指定模型存储路径
    OLLAMA_MODELS=D:\ollama\models
    
    # 允许跨域访问
    OLLAMA_ORIGINS=*
    
    # 允许外部访问 OLLAMA
    OLLAMA_HOST=0.0.0.0
    

    配置完成后,请重启系统或终端以使环境变量生效。

    6. 安装 Cherry Studio 客户端

    Cherry Studio 是一款支持 OLLAMA 的 AI 开发工具。安装 Cherry Studio 后,可启用 OLLAMA 支持并配置下载的模型。

  • 通过ImageMagick 转换HEIC格式为JPG格式图片

    ImageMagick是一个功能强大的图像处理软件套件,支持从命令行批量处理和转换图像。它支持超过 200 种图像格式,包括 JPEG、PNG、GIF、TIFF、HEIC、PDF、SVG 等,广泛应用于图像转换、编辑、处理和优化。通过 ImageMagick,我们可以轻松、快速地将 HEIC 格式图片转换为 JPG 格式,避免了因格式兼容问题带来的困扰。无论是处理少量还是大量图像,ImageMagick 都是一个高效且可靠的工具。

    ImageMagick常见转换操作

    1. 单个文件格式转换

    将 HEIC 格式转换为 JPG 格式:

    magick input.heic output.jpg

    这会将 input.heic 转换为 output.jpg。ImageMagick 会自动根据文件扩展名决定输出格式。

    2. 批量文件格式转换

    使用 mogrify 进行批量处理:

    magick mogrify -format jpg *.heic

    此命令将当前目录下所有 .heic 文件转换为 .jpg 格式,并保存在相同目录中。

    • -format jpg:指定输出格式为 JPG。
    • *.heic:指定输入文件,* 是通配符,表示所有 HEIC 文件。

    3. 指定输出目录

    为了将转换后的文件保存在另一个目录中,可以使用 -path 选项:

    mkdir output
    magick mogrify -path output -format jpg *.heic
    • -path output:指定输出目录为 output 文件夹。

    4. 调整输出质量

    可以通过 -quality 选项来控制输出图像的质量,适用于压缩 JPG 等格式。

    magick mogrify -quality 85 -format jpg *.heic
    • -quality 85:设置输出 JPG 文件的质量为 85(数值越大,质量越高,文件也越大)。

    5. 单个图像的格式转换与参数调整

    如果你需要调整图像的尺寸或其他属性,可以在命令中加入更多选项。例如,调整图片大小:

    magick input.heic -resize 800x600 output.jpg
    • -resize 800x600:将输出图像调整为 800×600 像素。

    6. 批量转换并调整图像尺寸

    如果你有多个 HEIC 文件,并且想在转换为 JPG 的同时调整图像大小,可以结合使用:

    magick mogrify -resize 800x600 -quality 85 -format jpg *.heic
    • -resize 800x600:调整所有图像的尺寸为 800×600。
    • -quality 85:设置输出质量为 85%。
  • DD命令测试硬盘读写速度

    1. 测试硬盘写入速度

    使用 dd 命令将随机数据写入硬盘并计算写入速度:

    dd if=/dev/zero of=/tmp/testfile bs=1M count=1024 oflag=direct
    
    • if=/dev/zero: 输入文件是 /dev/zero,这会生成零字节。
    • of=/tmp/testfile: 输出文件路径,即测试写入数据的目标文件。
    • bs=1M: 设置块大小为 1MB。
    • count=1024: 写入 1024 块(即 1024MB = 1GB)。
    • oflag=direct: 禁用文件系统缓存,进行原始写入操作。

    完成后,它会显示写入速度。

    2. 测试硬盘读取速度

    使用 dd 命令读取数据并计算读取速度:

    dd if=/tmp/testfile of=/dev/null bs=1M count=1024 iflag=direct
    
    • if=/tmp/testfile: 输入文件是先前创建的测试文件。
    • of=/dev/null: 输出数据丢弃,不保存。
    • bs=1M: 设置块大小为 1MB。
    • count=1024: 读取 1024 块(即 1024MB = 1GB)。
    • iflag=direct: 禁用文件系统缓存,进行原始读取操作。

    3. 清理测试文件

    rm -f /tmp/testfile
  • IPMI密码重置与恢复默认配置指南

    1. 使用IPMICFG工具重置IPMI密码

    前提条件

    • 确保您已经安装了IPMICFG工具(适用于您的操作系统)。
    • 拥有服务器的管理员权限。

    操作步骤

    1.1 查看当前用户信息

    使用以下命令列出所有IPMI用户:

    ipmicfg -user list
    

    示例输出:

    Maximum number of Users          : 10
    Count of currently enabled Users : 1
    User ID | User Name | Privilege Level | Enable
    --------|-----------|-----------------|-------
    2       | admin     | Administrator   | Yes
    

    此处可以看到admin用户的ID为2。

    1.2 重置管理员密码

    使用以下命令为用户ID为2的admin用户设置新密码:

    ipmicfg -user setpwd 2 新密码
    

    示例:

    ipmicfg -user setpwd 2 StrongPassword123
    

    注意:确保新密码符合安全策略(例如包含大小写字母、数字和特殊字符)。

    1.3 验证新密码

    通过IPMI的Web界面或ipmitool工具验证新密码是否生效:

    ipmitool -I lanplus -H <IPMI_IP地址> -U admin -P 新密码 chassis power status
    

    如果命令返回主机电源状态,则表示密码重置成功。

    2. 使用IPMICFG工具恢复默认配置

    前提条件

    • 所有用户配置和网络设置将被清除,请确保已备份重要信息。
    • 操作可能需要服务器管理员权限。

    操作步骤

    2.1 恢复出厂设置

    运行以下命令将IPMI配置恢复为默认值:

    ipmicfg -fd
    

    执行该命令后,所有IPMI配置(包括用户账号、密码和网络配置)将恢复为出厂设置。

    2.2 重启IPMI(可选)

    某些情况下需要重启IPMI以使更改生效:

    ipmicfg -rst
    

    2.3 重新配置IPMI

    • 重新设置管理员密码: 使用步骤1.2中的方法设置管理员密码。
    • 重新配置网络: 如果需要为IPMI设置静态IP地址,请使用以下命令:
    ipmicfg -dhcp off
    ipmicfg -ip <IP地址>
    ipmicfg -m <子网掩码>
    ipmicfg -gw <网关>

    示例:

    ipmicfg -dhcp off
    ipmicfg -ip 192.168.1.100
    ipmicfg -m 255.255.255.0
    ipmicfg -gw 192.168.1.1

    注意事项

    • 确保恢复默认配置前了解可能的影响,例如需要重新配置IPMI所有设置。
    • 如遇任何问题,请参考主板或服务器的官方文档或联系技术支持。

    3. 参考命令汇总

    • 查看用户列表:ipmicfg -user list
    • 重置用户密码:ipmicfg -user setpwd <UserID> <NewPassword>
    • 恢复默认配置:ipmicfg -fd
    • 重启IPMI服务:ipmicfg -rst
  • Visual Studio Code配置Ollama


    1. 安装Ollama

    1.1 下载Ollama

    https://ollama.com/
    

    1.2 使用终端下载模型

    ollama pull gemma2:9b   
    ollama pull llama3.1:8b
    ollama pull llama3.2:3b
    ollama pull qwen2.5:7b
    ollama pull qwen2.5:3b
    ollama pull gemma2:2b
    

    2. 安装Visual Studio Code

    2.1 下载Visual Studio Code

    https://code.visualstudio.com/
    

    2.2.Visual Studio Code安装Continue 插件

    2.3 配置插件config

    {
      "models": [
        {
          "title": "Llama 3.1 8B",
          "provider": "ollama",
          "model": "llama3.1:8b"
        },
        {
          "title": "llama3.2:3b",
          "provider": "ollama",
          "model": "llama3.2:3b"
        },
        {
          "title": "qwen2.5:7b",
          "provider": "ollama",
          "model": "qwen2.5:7b"
        },
        {
          "title": "qwen2.5:3b",
          "provider": "ollama",
          "model": "qwen2.5:3b"
        },
        {
          "title": "gemma2:2b",
          "provider": "ollama",
          "model": "gemma2:2b"
        },
        {
          "title": "gemma2:9b",
          "provider": "ollama",
          "model": "gemma2:9b"
        },
      ],
      "customCommands": [
        {
          "name": "test",
          "prompt": "{{{ input }}}\n\nWrite a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.",
          "description": "Write unit tests for highlighted code"
        }
      ],
      "contextProviders": [
        {
          "name": "code",
          "params": {}
        },
        {
          "name": "docs",
          "params": {}
        },
        {
          "name": "diff",
          "params": {}
        },
        {
          "name": "terminal",
          "params": {}
        },
        {
          "name": "problems",
          "params": {}
        },
        {
          "name": "folder",
          "params": {}
        },
        {
          "name": "codebase",
          "params": {}
        }
      ],
      "slashCommands": [
        {
          "name": "edit",
          "description": "Edit selected code"
        },
        {
          "name": "comment",
          "description": "Write comments for the selected code"
        },
        {
          "name": "share",
          "description": "Export the current chat session to markdown"
        },
        {
          "name": "cmd",
          "description": "Generate a shell command"
        },
        {
          "name": "commit",
          "description": "Generate a git commit message"
        }
      ],
    }
  • Web日志分析工具

    1. Web日志分析工具

    一年前,我写了一个Web日志进行分析并按照分类导出到Excel的工具
    一年后,我在AI的加持帮助下对这个工具代码进行了重构
    通过Web页面的方式进行呈现,支持了更多的日志解析,更灵活的使用

    2. 为什么要重构这个工具

    想法

    1. 日志分析的过程中总会遇到性能瓶颈和查找指定相关数据的瓶颈,最终只需要日志中的部分数据,只需要一个小工具获取想要的结果就可以了,暂时不考虑重量级产品全流量匹配
    2. 会遇到跨平台代码处理的问题,既然都跨平台了直接搞个Web版本更直观
    3. 无论是流量匹配还是日志匹配,归根结底还是通过正则表达式的方式去做,随着AI时代的猛烈迭代,我尝试使用AI去编码看看能否给我一个其它的处理数据的方式,尝试了不同的AI产品,例如LLAMA3,ChatGPT,Gemini等相关AI产品,最终的结论依旧是通过正则表达式的方式匹配
    4. 既然使用正则表达式的方式,我依旧在想能不能搞个自动匹配的方式实现,只需要上传日志脚本自动分析匹配最后输出结果,但事实是其实电脑也同样看不懂猜不出来日志中的每个字段究竟是什么,希望未来的AI可以猜出来人类想要什么,哪怕是给个选项!
    5. 在可以输出结果的同时顺便统计出次数数量方便进行统计工作,开始打算使用图表的方式去做最终没做,无法适配不同的图表不如给出输出的数据自己用其它的工具去做,这样更灵活
    6. 在第一版的Web日志分析工具中,输出的是Excel格式,这次直接被我取消了
      1. 原因1:Excel文件需要依赖Microsoft Excel或其他兼容工具
      2. 原因2:Excel在处理非常大的数据集时可能会变得非常缓慢
      3. 原因3:而如果需要人类易读的格式,并且计划由用户手动操作数据,Excel可能是更方便的选择,但它的处理能力不如CSV和JSON强大
    7. 这次使用的CSV和JSON格式进行输出
      1. CSV格式兼容性更好,几乎所有的数据分析工具、数据库和编程语言都能轻松处理CSV格式。它是一种通用的文本格式,可以在不同平台和工具之间无缝传输
      2. CSV文件结构简单,数据存储在纯文本中,适合快速读取和处理,如果后续有其它工具进行调用的话更方便
      3. 为什么增加了JSON呢,因为简单易懂:JSON采用键值对的形式,结构清晰,容易理解。即使没有专业的编程背景的人也能很快理解JSON的数据结构,JSON跨平台兼容性也不错

    实现过程

    参考了几个框架最终选择的streamlit框架,别问为啥,对我来说无论用什么好用就行

    按照思路:
    第一步上传文件能不能上传成功,日志大小问题
    第二步使用正则匹配日志,能不能匹配上选择的日志,反反复复的找了很多日志去测试,确实非常耗时的
    第三步输出日志,能输出结果就好了,别输出报错或者无法下载就行

    代码量虽然不大但中途改了很多次代码,主要改的都是正则表达式用来匹配日志的,中途反反复复的匹配日志,最终这事儿成了

    中途遇到的困难

    1. 困难还是有的,一年前第一版的Web日志分析工具改正则表达式匹配日志确实挺难的,没想打一年后有AI可以帮我干了,在LLAMA3,ChatGPT,Gemini等相关AI工具的帮助下写正则表达式方便多了,每家AI输出的正则表达式大同小异,不行就多试几次比手写快
    2. 中途大胆的做了些尝试,把我的代码全部交给AI例如LLAMA3,ChatGPT,Gemini等相关AI产品,让AI帮我修改全部的代码重构去实现最初的想法只需要上传日志脚本自动分析匹配最后输出结果,但结果是不行,等以后有其它的方式可以做到我在升级V3版本的Web日志分析工具吧,目前确实不太好做
    3. 既然没办法实现全自动的方式,最终把Apache、Nginx、IIS和Tomcat常用的日志格式进行正则表达式进行匹配,不像V1版本一样只处理Nginx日志
    4. 希望未来的AI工具更好用,以后有条件了一定搞个全自动的,因为目前增加新的日志格式的话还是要增加正则的规则,我不想改正则了。

    未来的计划(还没实现)

    1. 上传日志,脚本全自动分析匹配,输出结果
    2. 上传日志,解析全部日志字段,手动打类型标签,输出结果

    3. 功能介绍

    1. 支持多种日志格式:支持Apache、Nginx、IIS和Tomcat日志格式。
    2. 自动识别日志格式:自动识别日志格式,无需手动配置。
    3. 高效分析:快速分析大型日志文件,获取有关网站访问量、用户行为和系统性能的详细信息。
    4. 灵活筛选:支持多种筛选条件,例如日期范围、请求方法、请求URL等。
    5. 详细统计:支持多种统计条件,例如请求方法、请求URL、状态码等。
    6. 导出文件:支持导出CSV和JSON文件,方便进一步分析和处理。

    4. 项目地址

    以下是完成的Python代码

    # Author: liqixin
    # Mail: [email protected]
    # Web: https://www.qixinlee.com
    
    import streamlit as st
    import pandas as pd
    import json
    import logging
    import os
    import re
    from datetime import datetime
    from typing import List, Dict
    
    # 设置日志级别和格式
    logging.basicConfig(filename='log.log', format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)
    
    # 定义日志解析器的基类
    class LogParser:
        def __init__(self, log_file: bytes):
            self.log_file = log_file
    
        def _parse_log(self, log_pattern: re.Pattern) -> List[Dict]:
            parsed_log_entries = []
            for line in self.log_file:
                line = line.decode('utf-8')
                match = log_pattern.findall(line)
                if match:
                    for match_item in match:
                        request = match_item[3]
                        request_parts = request.split(' ', 2)
                        if len(request_parts) == 3:
                            request_method, request_url, http_protocol = request_parts
                        else:
                            request_method = request_parts[0]
                            request_url = ''
                            http_protocol = ''
                        parsed_log_entries.append({
                            'remote_addr': match_item[0],
                            'remote_user': match_item[1],
                            'time_local': match_item[2],
                            'request_method': request_method,
                            'request_url': request_url,
                            'http_protocol': http_protocol,
                            'status': match_item[4],
                            'body_bytes_sent': match_item[5],
                            'http_referer': match_item[6],
                            'http_user_agent': match_item[7]
                        })
            return parsed_log_entries
    
        def parse(self) -> List[Dict]:
            raise NotImplementedError("必须实现解析方法")
    
    # Apache 日志解析器
    class ApacheLogParser(LogParser):
        def parse(self) -> List[Dict]:
            log_pattern = re.compile(r'(\S+) - (\S+) \[(.*?)\] "(.*?)" (\d+) (\d+) "(.*?)" "(.*?)"', re.DOTALL)
            return self._parse_log(log_pattern)
    
    # Nginx 日志解析器
    class NginxLogParser(LogParser):
        def parse(self) -> List[Dict]:
            log_pattern = re.compile(r'(\S+) - - \[(.*?)\] "(.*?)" (\d+) (\d+) "(.*?)" "(.*?)"', re.DOTALL)
            return self._parse_log(log_pattern)
    
    # IIS 日志解析器
    class IISLogParser(LogParser):
        def parse(self) -> List[Dict]:
            log_pattern = re.compile(r'(\S+) (\S+) (\S+) \[(.*?)\] "(.*?)" (\d+) (\d+) "(.*?)" "(.*?)"', re.DOTALL)
            return self._parse_log(log_pattern)
    
    # Tomcat 日志解析器
    class TomcatLogParser(LogParser):
        def parse(self) -> List[Dict]:
            log_pattern = re.compile(r'(\S+) (\S+) \[(.*?)\] "(.*?)" (\d+) (\d+) "(.*?)" "(.*?)"', re.DOTALL)
            return self._parse_log(log_pattern)
    
    # 通用日志解析方法
    class LogParserFactory:
        @staticmethod
        def create_parser(log_type: str, log_file: bytes) -> LogParser:
            if log_type == 'Apache':
                return ApacheLogParser(log_file)
            elif log_type == 'Nginx':
                return NginxLogParser(log_file)
            elif log_type == 'IIS':
                return IISLogParser(log_file)
            elif log_type == 'Tomcat':
                return TomcatLogParser(log_file)
            else:
                raise ValueError(f"不支持的日志类型: {log_type}")
    
    # 定义导出文件函数
    def export_file(df: pd.DataFrame, export_format: str) -> str:
        try:
            current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
            if export_format == 'CSV(逗号分隔值)':
                csv_file_path = f'log_entries_{current_time}.csv'
                df.to_csv(csv_file_path, index=False)
                return csv_file_path
            elif export_format == 'JSON(JavaScript对象表示法)':
                json_file_path = f'log_entries_{current_time}.json'
                df['time_local'] = df['time_local'].apply(lambda x: x.isoformat())
                df['date'] = df['date'].apply(lambda x: x.isoformat())
                with open(json_file_path, 'w', encoding='utf-8') as json_file:
                    json.dump(df.to_dict(orient='records'), json_file, indent=4)
                return json_file_path
        except Exception as e:
            logging.error(f"导出文件失败:{e}")
            raise
    
    # 定义Web应用程序
    def main():
        # 设置主题
        st.set_page_config(layout="wide", page_title="安全运营 - Web日志分析工具")
        st.markdown("<style>body {background-color: #2F4F4F; color: #66D9EF;}</style>", unsafe_allow_html=True)
        st.markdown("<h1 style='text-align: center;'>安全运营 - Web日志分析工具</h1>", unsafe_allow_html=True)
    
        # 上传日志文件
        st.subheader('请选择日志文件')
        log_file = st.file_uploader('请选择日志文件', type=['log'])
        log_type = st.selectbox("请选择日志类型", ['Apache', 'Nginx', 'IIS', 'Tomcat'])
    
        if log_file is not None:
            try:
                # 使用工厂模式创建适当的日志解析器
                log_parser = LogParserFactory.create_parser(log_type, log_file)
                parsed_log_entries = log_parser.parse()
    
                if parsed_log_entries:
                    # 日志解析结果
                    st.subheader('日志解析结果')
                    df = pd.DataFrame(parsed_log_entries)
                    df['time_local'] = df['time_local'].apply(lambda x: datetime.strptime(x, '%d/%b/%Y:%H:%M:%S %z'))
                    df['date'] = df['time_local'].dt.date
                    st.write(df)
    
                    # 选择日期范围
                    st.subheader('请选择日期范围')
                    col1, col2 = st.columns(2)
                    start_date = col1.date_input('请选择开始日期')
                    end_date = col2.date_input('请选择结束日期')
                    filtered_df = df[(df['time_local'].dt.date >= start_date) & 
                                     (df['time_local'].dt.date <= end_date)]
    
                    # 筛选功能
                    st.subheader('筛选功能')
                    st.subheader('请选择筛选条件')
                    columns = filtered_df.columns.tolist()
                    select_columns = st.multiselect('请选择筛选字段', columns)
                    select_values = {}
                    for column in select_columns:
                        if column == 'date':
                            select_values[column] = st.date_input(f'请选择{column}值')
                        else:
                            select_values[column] = st.selectbox(f'请选择{column}值', filtered_df[column].unique())
                    filtered_df = filtered_df
                    for column, value in select_values.items():
                        if column == 'date':
                            filtered_df = filtered_df[filtered_df[column] == value]
                        else:
                            filtered_df = filtered_df[filtered_df[column] == value]
    
                    st.write(filtered_df)
    
                    # 统计功能
                    st.subheader('统计功能')
                    rank_columns = filtered_df.columns.tolist()
                    rank_columns_selected = st.multiselect('请选择统计字段', rank_columns)
    
                    if rank_columns_selected:
                        for i, column in enumerate(rank_columns_selected):
                            rank_df = filtered_df[column].value_counts().reset_index()
                            rank_df.columns = [column, '次数']
                            
                            if i % 2 == 0:
                                col1, col2 = st.columns(2)
                                col1.write(rank_df)
                            else:
                                col2.write(rank_df)
                    else:
                        st.write("请选择至少一个字段")
                    
                    # 导出文件功能
                    st.subheader('请选择导出格式')
                    export_format = st.selectbox('请选择导出格式', ['CSV(逗号分隔值)', 'JSON(JavaScript对象表示法)'])
                    if export_format:
                        file_path = export_file(filtered_df, export_format)
                        with open(file_path, 'rb') as file:
                            if export_format == 'CSV(逗号分隔值)':
                                st.download_button('下载CSV文件', file, file_path.split('/')[-1])
                            elif export_format == 'JSON(JavaScript对象表示法)':
                                st.download_button('下载JSON文件', file, file_path.split('/')[-1])
    
                else:
                    st.error('没有匹配的日志条目')
            except Exception as e:
                st.error(f"错误:{e}")
    
    if __name__ == '__main__':
        main()