阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Docker安装ELK并实现JSON格式日志分析

478次阅读
没有评论

共计 10716 个字符,预计需要花费 27 分钟才能阅读完成。

ELK 是什么

ELK 是 elastic 公司提供的一套完整的日志收集以及前端展示的解决方案,是三个产品的首字母缩写,分别是 ElasticSearch、Logstash 和 Kibana。

其中 Logstash 负责对日志进行处理,如日志的过滤、日志的格式化等;ElasticSearch 具有强大的文本搜索能力,因此作为日志的存储容器;而 Kibana 负责前端的展示。

ELK 搭建架构如下图:
Docker 安装 ELK 并实现 JSON 格式日志分析

加入了 filebeat 用于从不同的客户端收集日志,然后传递到 Logstash 统一处理。

ELK 的搭建

因为 ELK 是三个产品,可以选择依次安装这三个产品。

这里选择使用 Docker 安装 ELk。

Docker 安装 ELk 也可以选择分别下载这三个产品的镜像并运行,但是本次使用直接下载 elk 的三合一镜像来安装。

因此首先要保证已经有了 Docker 的运行环境,Docker 运行环境的搭建请查看:https://blog.csdn.net/qq1311256696/article/details/85277220

拉取镜像

有了 Docker 环境之后,在服务器运行命令:

docker pull sebp/elk

这个命令是在从 Docker 仓库下载 elk 三合一的镜像,总大小为 2 个多 G,如果发现下载速度过慢,可以将 Docker 仓库源地址替换为国内源地址。

下载完成之后,查看镜像:

docker images

Docker 安装 ELK 并实现 JSON 格式日志分析

Logstash 配置

/usr/config/logstash 目录下新建 beats-input.conf,用于日志的输入:

input {
  beats {port => 5044}
}

新建 output.conf,用于日志由 Logstash 到 ElasticSearch 的输出:

output {
  elasticsearch {hosts => ["localhost"]
    manage_template => false
    index => "%{[@metadata][beat]}"
  }
}

其中的 index 为输出到 ElasticSearch 后的index

运行容器

有了镜像之后直接启动即可:

docker run -d -p 5044:5044 -p 5601:5601 -p 9203:9200 -p 9303:9300 -v /var/data/elk:/var/lib/elasticsearch -v /usr/config/logstash:/etc/logstash/conf.d --name=elk sebp/elk

- d 的意思是后台运行容器;

- p 的意思是宿主机端口: 容器端口,即将容器中使用的端口映射到宿主机上的某个端口,ElasticSearch 的默认端口是 9200 和 9300,由于我的机器上已经运行了 3 台 ElasticSearch 实例,因此此处将映射端口进行了修改;

- v 的意思是宿主机的文件 | 文件夹: 容器的文件 | 文件夹,此处将容器中 elasticsearch 的数据挂载到宿主机的 /var/data/elk 上,以防容器重启后数据的丢失;并且将 logstash 的配置文件挂载到宿主机的 /usr/config/logstash 目录。

–name 的意思是给容器命名,命名是为了之后操作容器更加方便。

如果你之前搭建过 ElasticSearch 的话,会发现搭建的过程中有各种错误,但是使用 docker 搭建 elk 的过程中并没有出现那些错误。

运行后查看容器:

docker ps

Docker 安装 ELK 并实现 JSON 格式日志分析

查看容器日志:

docker logs -f elk

进入容器:

docker exec -it elk /bin/bash

修改配置后重启容器:

docker restart elk

查看 kinaba

浏览器输入 http://my_host:5601/
即可看到 kinaba 界面。此时 ElasticSearch 中还没有数据,需要安装 Filebeat 采集数据到 elk 中。

Filebeat 搭建

Filebeat 用于采集数据并上报到 Logstash 或者 ElasticSearch,在需要采集日志的服务器上下载 Filebeat 并解压即可使用

wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.2.1-linux-x86_64.tar.gz

tar -zxvf filebeat-6.2.1-linux-x86_64.tar.gz

修改配置文件

进入 filebeat,修改 filebeat.yml。

filebeat.prospectors:
- type: log
  #需要设置为 true 配置才能生效
  enabled: true
  path:
    #配置需要采集的日志路径
    - /var/log/*.log
  #可以打一个 tag 以后分类使用
  tag: ["my_tag"]
  #对应 ElasticSearch 的 type
  document_type: my_type
setup.kibana:
  #此处为 kibana 的 ip 及端口,即 kibana:5601
  host: ""
output.logstash:
  #此处为 logstash 的 ip 及端口,即 logstash:5044
  host: [""]
  #需要设置为 true,否则不生效
  enabled: true
#如果想直接从 Filebeat 采集数据到 ElasticSearch,则可以配置 output.elasticsearch 的相关配置

运行 Filebeat

运行:

./filebeat -e -c filebeat.yml -d "publish"

此时可以看到 Filebeat 会将配置的 path 下的 log 发送到 Logstash;然后在 elk 中,Logstash 处理完数据之后就会发送到 ElasticSearch。但我们想做的是通过 elk 进行数据分析,因此导入到 ElasticSearch 的数据必须是 JSON 格式的。

这是之前我的单条日志的格式:

 2019-10-22 10:44:03.441 INFO  rmjk.interceptors.IPInterceptor Line:248 - {"clientType":"1","deCode":"0fbd93a286533d071","eaType":2,"eaid":191970823383420928,"ip":"xx.xx.xx.xx","model":"HONOR STF-AL10","osType":"9","path":"/applicationEnter","result":5,"session":"ef0a5c4bca424194b29e2ff31632ee5c","timestamp":1571712242326,"uid":"130605789659402240","v":"2.2.4"}

导入之后不好分析,之后又想到使用 Logstash 的 filter 中的 grok 来处理日志使之变成 JSON 格式之后再导入到 ElasticSearch 中,但是由于我的日志中的参数是不固定的,发现难度太大了,于是转而使用 Logback,将日志直接格式化成 JSON 之后,再由 Filebeat 发送。

Logback 配置

我的项目是 Spring Boot,在项目中加入依赖:

<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  <version>5.2</version>
</dependency>

然后在项目中的 resource 目录下加入 logback.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--
       说明:1、日志级别及文件
           日志记录采用分级记录,级别与日志文件名相对应,不同级别的日志信息记录到不同的日志文件中
           例如:error 级别记录到 log_error_xxx.log 或 log_error.log(该文件为当前记录的日志文件),而 log_error_xxx.log 为归档日志,日志文件按日期记录,同一天内,若日志文件大小等于或大于 2M,则按 0、1、2... 顺序分别命名
           例如 log-level-2013-12-21.0.log
           其它级别的日志也是如此。2、文件路径
           若开发、测试用,在 Eclipse 中运行项目,则到 Eclipse 的安装路径查找 logs 文件夹,以相对路径../logs。若部署到 Tomcat 下,则在 Tomcat 下的 logs 文件中
       3、Appender
           FILEERROR 对应 error 级别,文件名以 log-error-xxx.log 形式命名
           FILEWARN 对应 warn 级别,文件名以 log-warn-xxx.log 形式命名
           FILEINFO 对应 info 级别,文件名以 log-info-xxx.log 形式命名
           FILEDEBUG 对应 debug 级别,文件名以 log-debug-xxx.log 形式命名
           stdout 将日志信息输出到控制上,为方便开发测试使用
    -->
    <contextName>service</contextName>
    <property name="LOG_PATH" value="logs"/>
    <!-- 设置系统日志目录 -->
    <property name="APPDIR" value="doctor"/>

    <!-- 日志记录器,日期滚动记录 -->
    <appender name="FILEERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${LOG_PATH}/${APPDIR}/log_error.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是 2013-12-21 日志,当前写的日志文件路径为 file 节点指定,可以将此文件与 file 指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而 2013-12-21 的日志文件在由 fileNamePattern 指定。%d{yyyy-MM-dd}指定日期格式,%i 指定索引 -->
            <fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日志文件不能超过 2M,若超过 2M,日志文件会以索引 0 开始,命名日志文件,例如 log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!-- 日志文件的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!-- 此日志文件只记录 info 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 日志记录器,日期滚动记录 -->
    <appender name="FILEWARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${LOG_PATH}/${APPDIR}/log_warn.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是 2013-12-21 日志,当前写的日志文件路径为 file 节点指定,可以将此文件与 file 指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而 2013-12-21 的日志文件在由 fileNamePattern 指定。%d{yyyy-MM-dd}指定日期格式,%i 指定索引 -->
            <fileNamePattern>${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日���文件不能超过 2M,若超过 2M,日志文件会以索引 0 开始,命名日志文件,例如 log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!-- 日志文件的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!-- 此日志文件只记录 info 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 日志记录器,日期滚动记录 -->
    <appender name="FILEINFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${LOG_PATH}/${APPDIR}/log_info.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是 2013-12-21 日志,当前写的日志文件路径为 file 节点指定,可以将此文件与 file 指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。而 2013-12-21 的日志文件在由 fileNamePattern 指定。%d{yyyy-MM-dd}指定日期格式,%i 指定索引 -->
            <fileNamePattern>${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日志文件不能超过 2M,若超过 2M,日志文件会以索引 0 开始,命名日志文件,例如 log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!-- 日志文件的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!-- 此日志文件只记录 info 级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="jsonLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${LOG_PATH}/${APPDIR}/log_IPInterceptor.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APPDIR}/log_IPInterceptor.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <jsonFactoryDecorator class="net.logstash.logback.decorate.CharacterEscapesJsonFactoryDecorator">
                <escape>
                    <targetCharacterCode>10</targetCharacterCode>
                    <escapeSequence>\u2028</escapeSequence>
                </escape>
            </jsonFactoryDecorator>
            <providers>
                <pattern>
                    <pattern>
                        {"timestamp":"%date{ISO8601}",
                        "uid":"%mdc{uid}",
                        "requestIp":"%mdc{ip}",
                        "id":"%mdc{id}",
                        "clientType":"%mdc{clientType}",
                        "v":"%mdc{v}",
                        "deCode":"%mdc{deCode}",
                        "dataId":"%mdc{dataId}",
                        "dataType":"%mdc{dataType}",
                        "vid":"%mdc{vid}",
                        "did":"%mdc{did}",
                        "cid":"%mdc{cid}",
                        "tagId":"%mdc{tagId}"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
    <!-- 彩色日志 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:-}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--encoder 默认配置为 PatternLayoutEncoder-->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!-- 此日志 appender 是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
    </appender>

    <!-- 指定项目中某个包,当有日志操作行为时的日志记录级别 -->
    <!-- rmjk.dao.mappe 为根包,也就是只要是发生在这个根包下面的所有日志操作行为的权限都是 DEBUG -->
    <!-- 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE  -->
    <logger name="rmjk.dao.mapper" level="DEBUG"/>
    <logger name="rmjk.service" level="DEBUG"/>
    <!-- 显示日志 -->
    <logger name="org.springframework.jdbc.core" additivity="false" level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILEINFO"/>
    </logger>
    <!-- 打印 json 日志   -->
    <logger name="IPInterceptor" level="info" additivity="false">
        <appender-ref ref="jsonLog"/>
    </logger>

    <!-- 生产环境下,将此级别配置为适合的级别,以免日志文件太多或影响程序性能 -->
    <root level="INFO">
        <appender-ref ref="FILEERROR"/>
        <appender-ref ref="FILEWARN"/>
        <appender-ref ref="FILEINFO"/>

        <!-- 生产环境将请 stdout,testfile 去掉 -->
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

其中的关键为:

<logger name="IPInterceptor" level="info" additivity="false">
      <appender-ref ref="jsonLog"/>
</logger>

在需要打印的文件中引入 slf4j:

 private static final Logger LOG = LoggerFactory.getLogger("IPInterceptor");

MDC 中放入需要打印的信息:

MDC.put("ip", ipAddress);
MDC.put("path", servletPath);
MDC.put("uid", paramMap.get("uid") == null ? "": paramMap.get("uid").toString());

此时如果使用了 LOG.info("msg") 的话,打印的内容会输入到日志的 message 中,日志格式如下:
Docker 安装 ELK 并实现 JSON 格式日志分析

修改 Logstash 配置

修改 /usr/config/logstash 目录下的 beats-input.conf:

input {
  beats {
    port => 5044
    codec => "json"
  }
}

只加了一句codec => "json",但是 Logstash 会按照 JSON 格式来解析输入的内容。

因为修改了配置,重启 elk:

docker restart elk

这样,当我们的日志生成完毕之后,使用 Filebeat 导入到 elk 中,就可以通过 Kibana 来进行日志分析了。

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-21发表,共计10716字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7992926
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
自己手撸一个AI智能体—跟创业大佬对话

自己手撸一个AI智能体—跟创业大佬对话

自己手撸一个 AI 智能体 — 跟创业大佬对话 前言 智能体(Agent)已经成为创业者和技术人绕...
300元就能买到的”小钢炮”?惠普7L四盘位小主机解析

300元就能买到的”小钢炮”?惠普7L四盘位小主机解析

  300 元就能买到的 ” 小钢炮 ”?惠普 7L 四盘位小主机解析 最近...
开发者福利:免费 .frii.site 子域名,一分钟申请即用

开发者福利:免费 .frii.site 子域名,一分钟申请即用

  开发者福利:免费 .frii.site 子域名,一分钟申请即用 前言 在学习 Web 开发、部署...
星哥带你玩飞牛NAS-7:手把手教你免费内网穿透-Cloudflare tunnel

星哥带你玩飞牛NAS-7:手把手教你免费内网穿透-Cloudflare tunnel

星哥带你玩飞牛 NAS-7:手把手教你免费内网穿透 -Cloudflare tunnel 前言 大家好,我是星...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装 Black 群晖 DSM7.2 系统安装教程(在 Vmware 虚拟机中、实体机均可)! 前言 大家好,...
安装并使用谷歌AI编程工具Antigravity(亲测有效)

安装并使用谷歌AI编程工具Antigravity(亲测有效)

  安装并使用谷歌 AI 编程工具 Antigravity(亲测有效) 引言 Antigravity...
你的云服务器到底有多强?宝塔跑分告诉你

你的云服务器到底有多强?宝塔跑分告诉你

你的云服务器到底有多强?宝塔跑分告诉你 为什么要用宝塔跑分? 宝塔跑分其实就是对 CPU、内存、磁盘、IO 做...
零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face 免费服务器 +Docker 快速部署 HertzBeat 监控平台 ...
星哥带你玩飞牛NAS-16:不再错过公众号更新,飞牛NAS搭建RSS

星哥带你玩飞牛NAS-16:不再错过公众号更新,飞牛NAS搭建RSS

  星哥带你玩飞牛 NAS-16:不再错过公众号更新,飞牛 NAS 搭建 RSS 对于经常关注多个微...