使用Elasticsearch + Logstash + Kibana搭建日志集中分析平臺實踐

jopen 9年前發布 | 26K 次閱讀 日志處理 ElasticSearch

在上周的上海Gopher Meetup的聚會上,聽了ASTA謝的演講。然后公司最近也需要實現一個日志集中分析平臺。ASTA謝恰好也講了他使用了Elasticsearch + Logstash + Kibana這個組合進行日志分析。回來之后就買了一本書然后各種google把它配置好了,當然只是把框架搭好了。這三個組建還有很多功能并沒有熟悉。本文只是簡單的介紹在Centos如果配置ELK(因為公司的服務器是Centos的,個人比較喜歡Ubuntu 哈哈)

什么是ELK:

Elasticsearch + Logstash + Kibana(ELK)是一套開源的日志管理方案,分析網站的訪問情況時我們一般會借助Google/百度/CNZZ等方式嵌入JS做數據統計,但是當網站訪問異常或者被攻擊時我們需要在后臺分析如Nginx的具體日志,而Nginx日志分割/GoAccess/Awstats都是相對簡單的單節點解決方案,針對分布式集群或者數據量級較大時會顯得心有余而力不足,而ELK的出現可以使我們從容面對新的挑戰。

  • Logstash:負責日志的收集,處理和儲存
  • Elasticsearch:負責日志檢索和分析
  • Kibana:負責日志的可視化

官方網站:

服務端配置:

安裝Java JDK:

cat /etc/redhat-release
//這是我linux的版本
CentOS Linux release 7.1.1503 (Core) 
//我們通過yum 方式安裝Java Jdk
yum install java-1.7.0-openjdk
cat/etc/redhat-release
//這是我linux的版本
CentOS Linuxrelease7.1.1503(Core)
//我們通過yum 方式安裝Java Jdk
yum installjava-1.7.0-openjdk

Elasticsearch安裝:

#下載安裝
wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.1.noarch.rpm
yum localinstall elasticsearch-1.7.1.noarch.rpm 

#啟動相關服務
service elasticsearch start
service elasticsearch status

#查看Elasticsearch的配置文件
rpm -qc elasticsearch

/etc/elasticsearch/elasticsearch.yml
/etc/elasticsearch/logging.yml
/etc/init.d/elasticsearch
/etc/sysconfig/elasticsearch
/usr/lib/sysctl.d/elasticsearch.conf
/usr/lib/systemd/system/elasticsearch.service
/usr/lib/tmpfiles.d/elasticsearch.conf

#查看端口使用情況
netstat -nltp

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1817/master         
tcp        0      0 0.0.0.0:5601            0.0.0.0:*               LISTEN      27369/node          
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      31848/nginx: master 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      16567/sshd          
tcp6       0      0 127.0.0.1:8005          :::*                    LISTEN      8263/java           
tcp6       0      0 :::5000                 :::*                    LISTEN      2771/java           
tcp6       0      0 :::8009                 :::*                    LISTEN      8263/java           
tcp6       0      0 :::3306                 :::*                    LISTEN      28839/mysqld        
tcp6       0      0 :::80                   :::*                    LISTEN      31848/nginx: master 
tcp6       0      0 :::8080                 :::*                    LISTEN      8263/java           
tcp6       0      0 :::9200                 :::*                    LISTEN      25808/java          
tcp6       0      0 :::9300                 :::*                    LISTEN      25808/java          
tcp6       0      0 :::9301                 :::*                    LISTEN      2771/java           
tcp6       0      0 :::22                   :::*                    LISTEN      16567/sshd
#下載安裝
wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.1.noarch.rpm
yum localinstallelasticsearch-1.7.1.noarch.rpm

#啟動相關服務
service elasticsearchstart
service elasticsearchstatus

#查看Elasticsearch的配置文件
rpm-qcelasticsearch

/etc/elasticsearch/elasticsearch.yml
/etc/elasticsearch/logging.yml
/etc/init.d/elasticsearch
/etc/sysconfig/elasticsearch
/usr/lib/sysctl.d/elasticsearch.conf
/usr/lib/systemd/system/elasticsearch.service
/usr/lib/tmpfiles.d/elasticsearch.conf

#查看端口使用情況
netstat-nltp

Active Internetconnections(onlyservers)
Proto Recv-QSend-QLocalAddress          ForeignAddress        State      PID/Programname    
tcp        0      0127.0.0.1:25            0.0.0.0:*              LISTEN      1817/master        
tcp        0      00.0.0.0:5601            0.0.0.0:*              LISTEN      27369/node          
tcp        0      00.0.0.0:80              0.0.0.0:*              LISTEN      31848/nginx:master
tcp        0      00.0.0.0:22              0.0.0.0:*              LISTEN      16567/sshd          
tcp6      0      0127.0.0.1:8005          :::*                    LISTEN      8263/java          
tcp6      0      0:::5000                :::*                    LISTEN      2771/java          
tcp6      0      0:::8009                :::*                    LISTEN      8263/java          
tcp6      0      0:::3306                :::*                    LISTEN      28839/mysqld        
tcp6      0      0:::80                  :::*                    LISTEN      31848/nginx:master
tcp6      0      0:::8080                :::*                    LISTEN      8263/java          
tcp6      0      0:::9200                :::*                    LISTEN      25808/java          
tcp6      0      0:::9300                :::*                    LISTEN      25808/java          
tcp6      0      0:::9301                :::*                    LISTEN      2771/java          
tcp6      0      0:::22                  :::*                    LISTEN      16567/sshd  

我們看到9200端口了說明我們安裝成功了,我們可以在終端輸入

#測試訪問
curl -X GET http://localhost:9200/
#測試訪問
curl-XGEThttp://localhost:9200/

或者直接瀏覽器打開我們可以看到

{
status: 200,
name: "Pip the Troll",
cluster_name: "elasticsearch",
version: {
number: "1.7.2",
build_hash: "e43676b1385b8125d647f593f7202acbd816e8ec",
build_timestamp: "2015-09-14T09:49:53Z",
build_snapshot: false,
lucene_version: "4.10.4"
},
tagline: "You Know, for Search"
}
{
status:200,
name:"Pip the Troll",
cluster_name:"elasticsearch",
version:{
number:"1.7.2",
build_hash:"e43676b1385b8125d647f593f7202acbd816e8ec",
build_timestamp:"2015-09-14T09:49:53Z",
build_snapshot:false,
lucene_version:"4.10.4"
},
tagline:"You Know, for Search"
}

說明我們的程序是運行正常的。

Kibana的安裝:

#下載tar包
wget https://download.elastic.co/kibana/kibana/kibana-4.1.1-linux-x64.tar.gz
#解壓
tar zxf kibana-4.1.1-linux-x64.tar.gz -C /usr/local/
cd /usr/local/
mv kibana-4.1.1-linux-x64 kibana

#創建kibana服務
vim /etc/rc.d/init.d/kibana

#!/bin/bash
### BEGIN INIT INFO
# Provides:          kibana
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Runs kibana daemon
# Description: Runs the kibana daemon as a non-root user
### END INIT INFO

# Process name
NAME=kibana
DESC="Kibana4"
PROG="/etc/init.d/kibana"

# Configure location of Kibana bin
KIBANA_BIN=/usr/local/kibana/bin

# PID Info
PID_FOLDER=/var/run/kibana/
PID_FILE=/var/run/kibana/$NAME.pid
LOCK_FILE=/var/lock/subsys/$NAME
PATH=/bin:/usr/bin:/sbin:/usr/sbin:$KIBANA_BIN
DAEMON=$KIBANA_BIN/$NAME

# Configure User to run daemon process
DAEMON_USER=root
# Configure logging location
KIBANA_LOG=/var/log/kibana.log

# Begin Script
RETVAL=0

if [ `id -u` -ne 0 ]; then
        echo "You need root privileges to run this script"
        exit 1
fi

# Function library
. /etc/init.d/functions

start() {
        echo -n "Starting $DESC : "

pid=`pidofproc -p $PID_FILE kibana`
        if [ -n "$pid" ] ; then
                echo "Already running."
                exit 0
        else
        # Start Daemon
if [ ! -d "$PID_FOLDER" ] ; then
                        mkdir $PID_FOLDER
                fi
daemon --user=$DAEMON_USER --pidfile=$PID_FILE $DAEMON 1>"$KIBANA_LOG" 2>&1 &
                sleep 2
                pidofproc node > $PID_FILE
                RETVAL=$?
                [[ $? -eq 0 ]] && success || failure
echo
                [ $RETVAL = 0 ] && touch $LOCK_FILE
                return $RETVAL
        fi
}

reload()
{
    echo "Reload command is not implemented for this service."
    return $RETVAL
}

stop() {
        echo -n "Stopping $DESC : "
        killproc -p $PID_FILE $DAEMON
        RETVAL=$?
echo
        [ $RETVAL = 0 ] && rm -f $PID_FILE $LOCK_FILE
}

case "$1" in
  start)
        start
;;
  stop)
        stop
        ;;
  status)
        status -p $PID_FILE $DAEMON
        RETVAL=$?
        ;;
  restart)
        stop
        start
        ;;
  reload)
reload
;;
  *)
# Invalid Arguments, print the following message.
        echo "Usage: $0 {start|stop|status|restart}" >&2
exit 2
        ;;
esac

#修改啟動權限
chmod +x /etc/rc.d/init.d/kibana

#啟動kibana服務
service kibana start
service kibana status

#查看端口
netstat -nltp
#下載tar包
wget https://download.elastic.co/kibana/kibana/kibana-4.1.1-linux-x64.tar.gz
#解壓
tar zxfkibana-4.1.1-linux-x64.tar.gz-C/usr/local/
cd/usr/local/
mv kibana-4.1.1-linux-x64kibana
#創建kibana服務
vim/etc/rc.d/init.d/kibana
#!/bin/bash
### BEGIN INIT INFO
# Provides:   kibana
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Runs kibana daemon
# Description: Runs the kibana daemon as a non-root user
### END INIT INFO
# Process name
NAME=kibana
DESC="Kibana4"
PROG="/etc/init.d/kibana"
# Configure location of Kibana bin
KIBANA_BIN=/usr/local/kibana/bin
# PID Info
PID_FOLDER=/var/run/kibana/
PID_FILE=/var/run/kibana/$NAME.pid
LOCK_FILE=/var/lock/subsys/$NAME
PATH=/bin:/usr/bin:/sbin:/usr/sbin:$KIBANA_BIN
DAEMON=$KIBANA_BIN/$NAME
# Configure User to run daemon process
DAEMON_USER=root
# Configure logging location
KIBANA_LOG=/var/log/kibana.log
# Begin Script
RETVAL=0
if[`id-u`-ne0];then
    echo"You need root privileges to run this script"
    exit1
fi
# Function library
./etc/init.d/functions
start(){
    echo-n"Starting $DESC : "
pid=`pidofproc-p$PID_FILEkibana`
    if[-n"$pid"];then
        echo"Already running."
        exit0
    else
    # Start Daemon
if[!-d"$PID_FOLDER"];then
            mkdir$PID_FOLDER
        fi
daemon--user=$DAEMON_USER--pidfile=$PID_FILE$DAEMON1>"$KIBANA_LOG"2>&1&
        sleep2
        pidofprocnode>$PID_FILE
        RETVAL=$?
        [[$?-eq0]]&&success||failure
echo
        [$RETVAL=0]&&touch$LOCK_FILE
        return$RETVAL
    fi
}
reload()
{
    echo"Reload command is not implemented for this service."
    return$RETVAL
}
stop(){
    echo-n"Stopping $DESC : "
    killproc-p$PID_FILE$DAEMON
    RETVAL=$?
echo
    [$RETVAL=0]&&rm-f$PID_FILE$LOCK_FILE
}
case"$1"in
  start)
    start
;;
  stop)
    stop
    ;;
  status)
    status-p$PID_FILE$DAEMON
    RETVAL=$?
    ;;
  restart)
    stop
    start
    ;;
  reload)
reload
;;
  *)
# Invalid Arguments, print the following message.
    echo"Usage: $0 {start|stop|status|restart}">&2
exit2
    ;;
esac
#修改啟動權限
chmod+x/etc/rc.d/init.d/kibana
#啟動kibana服務
service kibanastart
service kibanastatus
#查看端口
netstat-nltp

因為剛剛已經執行過

netstat -nltp
netstat-nltp

所以顯示的效果我這里就不貼了,如果我們能看到5601端口就說明我們安裝成功了。

Option 1:Generate SSL Certificates:

生成SSL證書是為了服務端和客戶端進行驗證:

sudo vi /etc/pki/tls/openssl.cnf
sudo vi/etc/pki/tls/openssl.cnf

Find the[ v3_ca ]section in the file, and add this line under it (substituting in the Logstash Server’s private IP address):

subjectAltName = IP: logstash_server_private_ip
subjectAltName=IP:logstash_server_private_ip
cd /etc/pki/tls
sudo openssl req -config /etc/pki/tls/openssl.cnf -x509 -days 3650 -batch -nodes -newkey rsa:2048 -keyout private/logstash-forwarder.key -out certs/logstash-forwarder.crt
cd/etc/pki/tls
sudo opensslreq-config/etc/pki/tls/openssl.cnf-x509-days3650-batch-nodes-newkeyrsa:2048-keyoutprivate/logstash-forwarder.key-outcerts/logstash-forwarder.crt

Option 2: FQDN (DNS):

cd /etc/pki/tls
sudo openssl req -subj '/CN=<^>logstash_server_fqdn/' -x509 -days 3650 -batch -nodes -newkey rsa:2048 -keyout private/logstash-forwarder.key -out certs/logstash-forwarder.crt
cd/etc/pki/tls
sudo opensslreq-subj'/CN=<^>logstash_server_fqdn/'-x509-days3650-batch-nodes-newkeyrsa:2048-keyoutprivate/logstash-forwarder.key-outcerts/logstash-forwarder.crt

Logstash安裝:

Logstash Forwarder(客戶端):

安裝Logstash Forwarder
wget https://download.elastic.co/logstash-forwarder/binaries/logstash-forwarder-0.4.0-1.x86_64.rpm
yum localinstall logstash-forwarder-0.4.0-1.x86_64.rpm

#查看logstash-forwarder的配置文件位置
rpm -qc logstash-forwarder
/etc/logstash-forwarder.conf

#備份配置文件
cp /etc/logstash-forwarder.conf /etc/logstash-forwarder.conf.save

#編輯 /etc/logstash-forwarder.conf,需要根據實際情況進行修改

vim /etc/logstash-forwarder.conf 
{
  "network": {
    "servers": [ "這里寫服務器的ip:5000" ],

    "ssl ca": "/etc/pki/tls/certs/logstash-forwarder.crt",

    "timeout": 15
  },

  "files": [
    {
      "paths": [
        "/var/log/messages",
        "/var/log/secure"
      ],

      "fields": { "type": "syslog" }
    }
  ]
}
安裝LogstashForwarder
wget https://download.elastic.co/logstash-forwarder/binaries/logstash-forwarder-0.4.0-1.x86_64.rpm
yum localinstalllogstash-forwarder-0.4.0-1.x86_64.rpm

#查看logstash-forwarder的配置文件位置
rpm-qclogstash-forwarder
/etc/logstash-forwarder.conf

#備份配置文件
cp/etc/logstash-forwarder.conf/etc/logstash-forwarder.conf.save

#編輯 /etc/logstash-forwarder.conf,需要根據實際情況進行修改

vim/etc/logstash-forwarder.conf
{
  "network":{
    "servers":["這里寫服務器的ip:5000"],

    "ssl ca":"/etc/pki/tls/certs/logstash-forwarder.crt",

    "timeout":15
  },

  "files":[
    {
      "paths":[
        "/var/log/messages",
        "/var/log/secure"
      ],

      "fields":{"type":"syslog"}
    }
  ]
}

Logstash Server(服務端):

#下載rpm包
wget https://download.elastic.co/logstash/logstash/packages/centos/logstash-1.5.4-1.noarch.rpm
#安裝
yum localinstall logstash-1.5.4-1.noarch.rpm 
#創建一個01-logstash-initial.conf文件
vim /etc/logstash/conf.d/01-logstash-initial.conf 
input {
  lumberjack {
    port => 5000
    type => "logs"
    ssl_certificate => "/etc/pki/tls/certs/logstash-forwarder.crt"
    ssl_key => "/etc/pki/tls/private/logstash-forwarder.key"
  }
}


filter {
  if [type] == "syslog" {
    grok {
      match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:[%{POSINT:syslog_pid}])?: %{GREEDYDATA:syslog_message}" }
      add_field => [ "received_at", "%{@timestamp}" ]
      add_field => [ "received_from", "%{host}" ]
    }
    syslog_pri { }
    date {
      match => [ "syslog_timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
    }
  }
}

output {
  elasticsearch { host => localhost }
  stdout { codec => rubydebug }
}

#啟動logstash服務
service logstash start
service logstash status

#訪問Kibana,Time-field name 選擇 @timestamp 要在下一步操作 Nginx 日志配置之后訪問 不然會沒有數據不能創建
http://localhost:5601/

#增加節點和客戶端配置一樣,注意同步證書(可以通過SSH的方式同步)
/etc/pki/tls/certs/logstash-forwarder.crt
#下載rpm包
wget https://download.elastic.co/logstash/logstash/packages/centos/logstash-1.5.4-1.noarch.rpm
#安裝
yum localinstalllogstash-1.5.4-1.noarch.rpm
#創建一個01-logstash-initial.conf文件
vim/etc/logstash/conf.d/01-logstash-initial.conf
input{
  lumberjack{
    port=>5000
    type=>"logs"
    ssl_certificate=>"/etc/pki/tls/certs/logstash-forwarder.crt"
    ssl_key=>"/etc/pki/tls/private/logstash-forwarder.key"
  }
}


filter{
  if[type]=="syslog"{
    grok{
      match=>{"message"=>"%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:[%{POSINT:syslog_pid}])?: %{GREEDYDATA:syslog_message}"}
      add_field=>["received_at","%{@timestamp}"]
      add_field=>["received_from","%{host}"]
    }
    syslog_pri{}
    date{
      match=>["syslog_timestamp","MMM  d HH:mm:ss","MMM dd HH:mm:ss"]
    }
  }
}

output{
  elasticsearch{host=>localhost}
  stdout{codec=>rubydebug}
}

#啟動logstash服務
service logstashstart
service logstashstatus

#訪問Kibana,Time-field name 選擇 @timestamp 要在下一步操作 Nginx 日志配置之后訪問 不然會沒有數據不能創建
http://localhost:5601/

#增加節點和客戶端配置一樣,注意同步證書(可以通過SSH的方式同步)
/etc/pki/tls/certs/logstash-forwarder.crt

配置Nginx日志:

#修改客戶端配置
vim /etc/logstash-forwarder.conf

{
  "network": {
    "servers": [ "自己服務器的ip地址:5000" ],

    "ssl ca": "/etc/pki/tls/certs/logstash-forwarder.crt",

    "timeout": 15
  },

  "files": [
    {
      "paths": [
        "/var/log/messages",
        "/var/log/secure"
      ],
      "fields": { "type": "syslog" }
    }, {
      "paths": [
        "/app/local/nginx/logs/access.log"
      ],
      "fields": { "type": "nginx" }
    }
  ]
}

#服務端增加patterns
mkdir /opt/logstash/patterns
vim /opt/logstash/patterns/nginx

NGUSERNAME [a-zA-Z.@-+_%]+
NGUSER %{NGUSERNAME}
NGINXACCESS %{IPORHOST:remote_addr} - - [%{HTTPDATE:time_local}] "%{WORD:method} %{URIPATH:path}(?:%{URIPARAM:param})? HTTP/%{NUMBER:httpversion}" %{INT:status} %{INT:body_bytes_sent} %{QS:http_referer} %{QS:http_user_agent}


#修改logstash權限
chown -R logstash:logstash /opt/logstash/patterns

#修改服務端配置
vim /etc/logstash/conf.d/01-logstash-initial.conf 

input {
  lumberjack {
    port => 5000
    type => "logs"
    ssl_certificate => "/etc/pki/tls/certs/logstash-forwarder.crt"
    ssl_key => "/etc/pki/tls/private/logstash-forwarder.key"
  }
}


filter {
  if [type] == "syslog" {
    grok {
      match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:[%{POSINT:syslog_pid}])?: %{GREEDYDATA:syslog_message}" }
      add_field => [ "received_at", "%{@timestamp}" ]
      add_field => [ "received_from", "%{host}" ]
    }
    syslog_pri { }
    date {
      match => [ "syslog_timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
    }
  }
  if [type] == "nginx" {
    grok {
       match => { "message" => "%{NGINXACCESS}" }
    }
  }
}

output {
  elasticsearch { host => localhost }
  stdout { codec => rubydebug }
}
#修改客戶端配置
vim/etc/logstash-forwarder.conf

{
  "network":{
    "servers":["自己服務器的ip地址:5000"],

    "ssl ca":"/etc/pki/tls/certs/logstash-forwarder.crt",

    "timeout":15
  },

  "files":[
    {
      "paths":[
        "/var/log/messages",
        "/var/log/secure"
      ],
      "fields":{"type":"syslog"}
    },{
      "paths":[
        "/app/local/nginx/logs/access.log"
      ],
      "fields":{"type":"nginx"}
    }
  ]
}

#服務端增加patterns
mkdir/opt/logstash/patterns
vim/opt/logstash/patterns/nginx

NGUSERNAME[a-zA-Z.@-+_%]+
NGUSER%{NGUSERNAME}
NGINXACCESS%{IPORHOST:remote_addr}--[%{HTTPDATE:time_local}]"%{WORD:method} %{URIPATH:path}(?:%{URIPARAM:param})? HTTP/%{NUMBER:httpversion}"%{INT:status}%{INT:body_bytes_sent}%{QS:http_referer}%{QS:http_user_agent}


#修改logstash權限
chown-Rlogstash:logstash/opt/logstash/patterns

#修改服務端配置
vim/etc/logstash/conf.d/01-logstash-initial.conf

input{
  lumberjack{
    port=>5000
    type=>"logs"
    ssl_certificate=>"/etc/pki/tls/certs/logstash-forwarder.crt"
    ssl_key=>"/etc/pki/tls/private/logstash-forwarder.key"
  }
}


filter{
  if[type]=="syslog"{
    grok{
      match=>{"message"=>"%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:[%{POSINT:syslog_pid}])?: %{GREEDYDATA:syslog_message}"}
      add_field=>["received_at","%{@timestamp}"]
      add_field=>["received_from","%{host}"]
    }
    syslog_pri{}
    date{
      match=>["syslog_timestamp","MMM  d HH:mm:ss","MMM dd HH:mm:ss"]
    }
  }
  if[type]=="nginx"{
    grok{
       match=>{"message"=>"%{NGINXACCESS}"}
    }
  }
}

output{
  elasticsearch{host=>localhost}
  stdout{codec=>rubydebug}
}

我們看一下完成配置之后的效果:

使用Elasticsearch + Logstash + Kibana搭建日志集中分析平臺實踐

好了,我是折騰了2天才折騰出來的,感覺自己好笨。寫篇總結為了下一次能夠快速的搭建起來。

我們可以ton

使用Elasticsearch + Logstash + Kibana搭建日志集中分析平臺實踐

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!