Qwen1.5-1.8B-GPTQ-Int4部署教程:vLLM服务与ELK日志系统的集成实践

1. 引言:为什么需要日志系统?

想象一下,你刚刚部署了一个AI模型服务,用户开始访问,一切看起来都很顺利。但突然,某个用户反馈说“模型回答很慢”,或者“返回的结果很奇怪”。这时候,你该怎么办?

如果没有日志系统,你就像在黑暗中摸索——不知道谁访问了服务、不知道请求耗时多久、不知道模型输出了什么内容。你只能凭感觉猜测问题所在,效率极低,而且很难定位到具体原因。

这就是为什么我们需要为AI模型服务搭建一个完善的日志系统。今天,我就来分享一个实战方案:如何为基于vLLM部署的Qwen1.5-1.8B-GPTQ-Int4模型服务,集成ELK(Elasticsearch、Logstash、Kibana)日志系统。

通过这个教程,你将学会:

  • 如何快速部署Qwen1.5-1.8B-GPTQ-Int4模型
  • 如何配置vLLM服务输出结构化日志
  • 如何搭建ELK日志收集和分析平台
  • 如何通过Kibana可视化监控模型服务

无论你是AI工程师、运维人员,还是对模型部署感兴趣的技术爱好者,这个教程都能让你掌握一套实用的日志监控方案。

2. 环境准备与快速部署

2.1 系统要求与前置条件

在开始之前,确保你的环境满足以下要求:

  • 操作系统:Ubuntu 20.04或更高版本(其他Linux发行版也可,但命令可能略有不同)
  • 内存:至少8GB RAM(模型本身约1.8GB,加上日志系统需要额外内存)
  • 存储:至少20GB可用空间
  • 网络:能够访问Docker Hub和GitHub
  • 基础软件:Docker和Docker Compose已安装

如果你不确定是否满足条件,可以运行以下命令检查:

# 检查系统信息
uname -a

# 检查内存
free -h

# 检查Docker
docker --version
docker-compose --version

2.2 一键部署Qwen1.5-1.8B-GPTQ-Int4模型

我们使用vLLM来部署模型,因为它提供了高性能的推理服务,并且支持GPTQ量化模型。以下是完整的部署步骤:

# 1. 创建工作目录
mkdir -p ~/qwen-deployment
cd ~/qwen-deployment

# 2. 创建docker-compose.yml文件
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  vllm-service:
    image: vllm/vllm-openai:latest
    container_name: qwen-vllm
    ports:
      - "8000:8000"
    volumes:
      - ./models:/models
      - ./logs:/var/log/vllm
    environment:
      - MODEL=/models/Qwen1.5-1.8B-Chat-GPTQ-Int4
      - HOST=0.0.0.0
      - PORT=8000
      - LOG_LEVEL=INFO
      - LOG_FORMAT=json  # 关键:输出JSON格式日志
    command: >
      --model /models/Qwen1.5-1.8B-Chat-GPTQ-Int4
      --served-model-name Qwen1.5-1.8B-Chat
      --host 0.0.0.0
      --port 8000
      --log-level INFO
      --log-format json
      --max-model-len 4096
    restart: unless-stopped
    networks:
      - elk-network

networks:
  elk-network:
    driver: bridge
EOF

# 3. 下载模型(如果已有模型文件,可以跳过这一步)
# 注意:你需要从合法渠道获取Qwen1.5-1.8B-Chat-GPTQ-Int4模型文件
# 将模型文件放置在 ./models/Qwen1.5-1.8B-Chat-GPTQ-Int4 目录下

# 4. 启动服务
docker-compose up -d

# 5. 检查服务状态
docker-compose logs -f vllm-service

2.3 验证模型部署成功

服务启动后,我们需要验证模型是否正常运行。有两种方法可以验证:

方法一:查看服务日志

# 查看实时日志
docker-compose logs -f vllm-service

# 或者查看日志文件
tail -f ~/qwen-deployment/logs/server.log

如果看到类似下面的输出,说明模型加载成功:

INFO 01-01 12:00:00 llm_engine.py:150] Initializing an LLM engine with config: ...
INFO 01-01 12:00:05 model_runner.py:85] Loading model weights...
INFO 01-01 12:00:30 llm_engine.py:280] Model loaded successfully.

方法二:通过API测试

# 使用curl测试模型服务
curl http://localhost:8000/v1/models

# 或者发送一个简单的请求
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen1.5-1.8B-Chat",
    "messages": [
      {"role": "user", "content": "你好,请介绍一下你自己"}
    ],
    "max_tokens": 100
  }'

如果API返回正常的响应,说明模型服务已经就绪。

3. 搭建ELK日志系统

现在模型服务已经运行起来了,接下来我们要搭建ELK系统来收集和分析日志。ELK是三个开源软件的缩写:

  • Elasticsearch:负责存储和检索日志数据
  • Logstash:负责收集、处理、转发日志
  • Kibana:提供Web界面,用于查询和可视化日志

3.1 创建ELK的Docker Compose配置

我们在同一个项目中添加ELK服务:

# 创建ELK的docker-compose配置
cat > docker-compose-elk.yml << 'EOF'
version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - ./elasticsearch/data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    networks:
      - elk-network
    restart: unless-stopped

  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    container_name: logstash
    volumes:
      - ./logstash/config:/usr/share/logstash/config
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./logs:/var/log/vllm:ro
    ports:
      - "5044:5044"
      - "5000:5000/tcp"
      - "5000:5000/udp"
      - "9600:9600"
    environment:
      - LS_JAVA_OPTS=-Xmx256m -Xms256m
    networks:
      - elk-network
    depends_on:
      - elasticsearch
    restart: unless-stopped

  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    networks:
      - elk-network
    depends_on:
      - elasticsearch
    restart: unless-stopped

networks:
  elk-network:
    driver: bridge
EOF

3.2 配置Logstash管道

Logstash需要配置文件来定义如何收集和处理日志。我们创建两个配置文件:

第一个文件:logstash配置

# 创建配置目录
mkdir -p ~/qwen-deployment/logstash/config
mkdir -p ~/qwen-deployment/logstash/pipeline

# 创建logstash.yml配置文件
cat > ~/qwen-deployment/logstash/config/logstash.yml << 'EOF'
http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]
EOF

# 创建pipeline配置文件
cat > ~/qwen-deployment/logstash/pipeline/logstash.conf << 'EOF'
input {
  # 从文件读取vLLM日志
  file {
    path => "/var/log/vllm/server.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    codec => json {
      charset => "UTF-8"
    }
    tags => ["vllm"]
  }
  
  # 也可以从TCP端口接收日志(备用方案)
  tcp {
    port => 5000
    codec => json_lines
    tags => ["vllm-tcp"]
  }
}

filter {
  # 如果是vLLM日志,进行特殊处理
  if "vllm" in [tags] {
    # 解析时间戳
    date {
      match => ["timestamp", "ISO8601"]
      target => "@timestamp"
    }
    
    # 添加服务标识
    mutate {
      add_field => {
        "service" => "qwen-vllm"
        "model" => "Qwen1.5-1.8B-Chat-GPTQ-Int4"
      }
    }
    
    # 处理请求日志
    if [message] =~ "Request received" {
      grok {
        match => { "message" => "Request received: %{DATA:request_id} %{DATA:method} %{DATA:path}" }
      }
      mutate {
        add_tag => ["request"]
      }
    }
    
    # 处理响应日志
    if [message] =~ "Request completed" {
      grok {
        match => { "message" => "Request completed: %{DATA:request_id} %{NUMBER:duration:float} seconds" }
      }
      mutate {
        add_tag => ["response"]
      }
    }
    
    # 处理错误日志
    if [log.level] == "ERROR" {
      mutate {
        add_tag => ["error"]
      }
    }
  }
}

output {
  # 输出到Elasticsearch
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "vllm-logs-%{+YYYY.MM.dd}"
    user => "elastic"
    password => "changeme"  # 生产环境请修改
  }
  
  # 同时输出到控制台(用于调试)
  stdout {
    codec => rubydebug
  }
}
EOF

3.3 启动ELK系统

现在我们可以启动ELK服务了:

# 启动ELK服务
cd ~/qwen-deployment
docker-compose -f docker-compose-elk.yml up -d

# 检查所有服务状态
docker-compose ps
docker-compose -f docker-compose-elk.yml ps

# 等待Elasticsearch启动(大约需要30-60秒)
sleep 60

# 检查Elasticsearch是否正常
curl http://localhost:9200/

# 检查Logstash是否正常
curl http://localhost:9600/

# 检查Kibana是否正常(在浏览器中访问)
# http://localhost:5601

如果一切正常,你应该能看到各个服务的健康状态信息。

4. 配置vLLM日志输出

为了让vLLM的日志能够被ELK系统收集,我们需要确保vLLM输出结构化的JSON日志。我们在部署时已经通过环境变量设置了LOG_FORMAT=json,现在让我们验证一下日志格式。

4.1 查看当前日志格式

# 查看vLLM的日志输出
docker-compose logs --tail=10 vllm-service

# 或者直接查看日志文件
cat ~/qwen-deployment/logs/server.log | head -5

你应该能看到类似这样的JSON格式日志:

{
  "timestamp": "2024-01-01T12:00:00.123456Z",
  "level": "INFO",
  "message": "Request received: req-123456 /v1/chat/completions",
  "module": "vllm.entrypoints.openai.api_server",
  "request_id": "req-123456"
}

4.2 增强vLLM日志输出(可选)

如果你需要更详细的日志信息,可以修改vLLM的启动参数。创建一个自定义的Python脚本来启动vLLM,这样可以更灵活地控制日志输出:

# 创建自定义启动脚本
cat > ~/qwen-deployment/start_vllm.py << 'EOF'
#!/usr/bin/env python3
import json
import logging
import sys
from vllm import AsyncLLMEngine, SamplingParams
from vllm.entrypoints.openai.api_server import run_server

# 配置JSON格式的日志
class JsonFormatter(logging.Formatter):
    def format(self, record):
        log_record = {
            "timestamp": self.formatTime(record),
            "level": record.levelname,
            "message": record.getMessage(),
            "module": record.module,
            "function": record.funcName,
            "line": record.lineno,
        }
        
        # 添加额外字段
        if hasattr(record, 'request_id'):
            log_record['request_id'] = record.request_id
        if hasattr(record, 'model'):
            log_record['model'] = record.model
        if hasattr(record, 'duration'):
            log_record['duration'] = record.duration
            
        return json.dumps(log_record)

# 设置日志
logger = logging.getLogger()
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)

if __name__ == "__main__":
    # 这里可以添加自定义的日志配置
    # 然后调用vLLM的run_server函数
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", type=str, default="0.0.0.0")
    parser.add_argument("--port", type=int, default=8000)
    parser.add_argument("--model", type=str, required=True)
    args = parser.parse_args()
    
    # 运行vLLM服务器
    run_server(
        host=args.host,
        port=args.port,
        model=args.model,
        served_model_name="Qwen1.5-1.8B-Chat"
    )
EOF

# 修改docker-compose.yml,使用自定义脚本
# 你可以更新volumes部分,挂载这个脚本,并修改command来使用它

4.3 测试日志收集

现在让我们测试一下日志是否能够正常收集到ELK中:

# 1. 首先,发送一些测试请求到vLLM服务
for i in {1..5}; do
  curl -s http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d "{
      \"model\": \"Qwen1.5-1.8B-Chat\",
      \"messages\": [
        {\"role\": \"user\", \"content\": \"测试请求 $i,当前时间是多少?\"}
      ],
      \"max_tokens\": 50
    }" > /dev/null
  echo "发送请求 $i"
  sleep 1
done

# 2. 检查Logstash是否收到日志
docker-compose -f docker-compose-elk.yml logs --tail=20 logstash

# 3. 检查Elasticsearch中是否有数据
curl -s "http://localhost:9200/vllm-logs-*/_search?pretty" | head -50

如果一切正常,你应该能在Elasticsearch中看到刚刚的请求日志。

5. 使用Kibana分析日志

ELK系统最强大的部分就是Kibana的可视化界面。让我们来配置Kibana,创建一些有用的仪表板。

5.1 首次访问Kibana

  1. 打开浏览器,访问 http://localhost:5601
  2. 首次访问时,Kibana可能会要求你配置默认设置,直接跳过即可
  3. 进入主页后,点击左侧菜单的"Discover"

5.2 创建索引模式

在Kibana中,我们需要先创建索引模式,才能查询数据:

  1. 点击左侧菜单的"Management"(齿轮图标)
  2. 选择"Stack Management"
  3. 点击"Index Patterns"
  4. 点击"Create index pattern"
  5. 输入索引名称:vllm-logs-*
  6. 点击"Next step"
  7. 选择时间字段:@timestamp
  8. 点击"Create index pattern"

5.3 使用Discover查询日志

现在你可以使用Discover功能来查询日志了:

  1. 点击左侧菜单的"Discover"
  2. 在左上角选择你刚创建的索引模式 vllm-logs-*
  3. 你会看到所有的日志事件
  4. 可以尝试以下查询:
    • 查找错误日志:log.level: ERROR
    • 查找特定请求:request_id: "req-123456"
    • 查找耗时长的请求:duration: >1.0

5.4 创建可视化图表

Kibana的可视化功能非常强大,让我们创建几个有用的图表:

图表1:请求数量随时间变化

  1. 点击左侧菜单的"Visualize"
  2. 点击"Create visualization"
  3. 选择"Line"图表类型
  4. 选择索引模式 vllm-logs-*
  5. 在"Metrics"中,选择"Count"作为Y轴
  6. 在"Buckets"中,选择"X-axis"
  7. 选择"Date Histogram"作为聚合方式,字段选择@timestamp
  8. 点击右上角的"Save",命名为"请求数量趋势"

图表2:请求耗时分布

  1. 点击"Create visualization"
  2. 选择"Vertical Bar"图表类型
  3. 选择索引模式 vllm-logs-*
  4. 在"Metrics"中,选择"Average"作为Y轴,字段选择duration
  5. 在"Buckets"中,选择"X-axis"
  6. 选择"Terms"作为聚合方式,字段选择request_id.keyword
  7. 设置Size为10(显示前10个请求)
  8. 点击"Save",命名为"请求耗时Top 10"

图表3:错误类型统计

  1. 点击"Create visualization"
  2. 选择"Pie"图表类型
  3. 选择索引模式 vllm-logs-*
  4. 添加过滤器:log.level: ERROR
  5. 在"Buckets"中,选择"Slice by"
  6. 选择"Terms"作为聚合方式,字段选择message.keyword
  7. 点击"Save",命名为"错误类型分布"

5.5 创建仪表板

将多个可视化图表组合成一个仪表板:

  1. 点击左侧菜单的"Dashboard"
  2. 点击"Create dashboard"
  3. 点击"Add"按钮
  4. 选择你刚才创建的所有可视化图表
  5. 调整图表的位置和大小
  6. 点击"Save",命名为"Qwen模型监控仪表板"

现在你有了一个完整的监控仪表板,可以实时查看:

  • 模型服务的请求量变化
  • 每个请求的耗时情况
  • 错误发生的情况和类型

6. 高级功能与优化建议

6.1 添加业务指标监控

除了系统日志,我们还可以监控一些业务指标,比如:

# 示例:在vLLM请求处理中添加自定义指标
import time
from functools import wraps

def log_metrics(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        start_time = time.time()
        
        try:
            result = await func(*args, **kwargs)
            duration = time.time() - start_time
            
            # 记录成功指标
            logging.info("Request processed successfully", extra={
                'request_id': kwargs.get('request_id', 'unknown'),
                'duration': duration,
                'status': 'success',
                'model': 'Qwen1.5-1.8B-Chat-GPTQ-Int4'
            })
            
            return result
        except Exception as e:
            duration = time.time() - start_time
            
            # 记录失败指标
            logging.error(f"Request failed: {str(e)}", extra={
                'request_id': kwargs.get('request_id', 'unknown'),
                'duration': duration,
                'status': 'error',
                'error_type': type(e).__name__,
                'model': 'Qwen1.5-1.8B-Chat-GPTQ-Int4'
            })
            
            raise
    
    return wrapper

6.2 设置日志轮转和清理

为了避免日志文件无限增长,我们需要设置日志轮转:

# 创建logrotate配置
cat > /etc/logrotate.d/vllm << 'EOF'
/root/qwen-deployment/logs/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 644 root root
    postrotate
        docker-compose -f /root/qwen-deployment/docker-compose.yml restart vllm-service
    endscript
}
EOF

# 测试logrotate配置
logrotate -d /etc/logrotate.d/vllm

# 手动运行一次
logrotate -f /etc/logrotate.d/vllm

6.3 配置告警规则

在Kibana中,我们可以设置告警规则,当出现问题时自动通知:

  1. 点击左侧菜单的"Management" → "Stack Management"
  2. 选择"Alerts and Insights" → "Rules"
  3. 点击"Create rule"
  4. 选择"Log threshold"
  5. 配置规则:
    • 检查索引:vllm-logs-*
    • 条件:当错误日志数量在5分钟内超过10条时触发
    • 操作:发送邮件或Webhook通知

6.4 性能优化建议

  1. Elasticsearch优化

    # 修改docker-compose-elk.yml中的elasticsearch配置
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms2g -Xmx2g"  # 根据内存调整
      - indices.query.bool.max_clause_count=4096
    
  2. Logstash优化

    # 增加Logstash的堆内存
    environment:
      - LS_JAVA_OPTS=-Xmx1g -Xms1g
    
  3. vLLM优化

    # 调整vLLM的批处理大小
    command: >
      --model /models/Qwen1.5-1.8B-Chat-GPTQ-Int4
      --served-model-name Qwen1.5-1.8B-Chat
      --host 0.0.0.0
      --port 8000
      --max-model-len 4096
      --tensor-parallel-size 1
      --gpu-memory-utilization 0.9
      --max-num-batched-tokens 2048  # 调整批处理大小
    

7. 总结

通过这个教程,我们完成了一个完整的AI模型服务日志监控方案。让我们回顾一下关键步骤:

7.1 核心成果

  1. 成功部署了Qwen1.5-1.8B-GPTQ-Int4模型:使用vLLM框架,提供了高性能的推理服务
  2. 搭建了ELK日志系统:实现了日志的收集、存储、分析和可视化
  3. 配置了结构化日志输出:确保vLLM输出JSON格式的日志,便于ELK处理
  4. 创建了监控仪表板:通过Kibana可以实时查看模型服务的运行状态
  5. 建立了完整的监控体系:从日志收集到告警通知,形成了闭环

7.2 实际价值

这个方案带来的实际价值包括:

  • 快速定位问题:当用户反馈问题时,可以通过请求ID快速找到相关日志
  • 性能监控:实时监控请求耗时,发现性能瓶颈
  • 错误分析:统计错误类型,指导模型优化方向
  • 容量规划:通过请求量趋势,预测资源需求
  • 服务质量保障:确保模型服务的稳定性和可靠性

7.3 下一步建议

如果你已经完成了基础部署,可以考虑以下进阶方向:

  1. 多模型支持:扩展ELK配置,支持监控多个模型服务
  2. 用户行为分析:在日志中添加用户ID,分析用户使用模式
  3. A/B测试支持:通过日志对比不同模型版本的效果
  4. 成本监控:记录每个请求的token使用量,计算API调用成本
  5. 自动化运维:结合告警系统,实现故障自动恢复

7.4 遇到问题怎么办?

如果在实施过程中遇到问题,可以:

  1. 检查服务状态:使用docker-compose psdocker-compose logs查看服务状态
  2. 验证网络连通性:确保各个容器之间可以互相访问
  3. 检查日志文件:查看具体的错误信息
  4. 调整配置参数:根据实际情况调整内存、超时时间等参数
  5. 查阅官方文档:vLLM、Elasticsearch、Logstash、Kibana都有详细的官方文档

记住,日志系统不是一次性的工作,而是一个持续优化的过程。随着业务的发展,你需要不断调整和优化监控策略。希望这个教程能帮助你建立起可靠的AI模型服务监控体系!


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

更多推荐