Docker-compose搭建ELK環境並同步MS SQL Server資料

aspritea發表於2021-06-11

前言

本文作為學習記錄,供大家參考:一次使用阿里雲(Aliyun)1核2G centos7.5 雲主機搭建Docker下的ELK環境,並匯入MS SQL Server的商品資料以供Kibana展示的配置過程。

關於Docker配置,本文直接使用開源專案 docker-elk(該專案維護了一個 Docker Compose 版的 Elastic Stack), 在其上做簡單修改。


參考文章:
SQL Server資料同步到ELK
使用 Docker 搭建 ELK 環境

1. 環境準備

1.1 Docker & Docker Compose

官網下載安裝 DockerDocker Compose

注:對於 Win/MacOS系統安裝 Docker 後自帶 Docker Compsoe,不需要再次安裝。

Docker安裝好後,建議先配置相關許可權,官方文件:linux-postinstall

1.2 docker-elk 專案

git clone https://github.com/deviantony/docker-elk.git /app/docker-elk

克隆到指定目錄,這裡指定到 /app/docker-elk

該專案初始結構(elk version 7.13)如下:
├── docker-compose.yml
├── docker-stack.yml
├── elasticsearch
│ ├── config
│ │ └── elasticsearch.yml
│ └── Dockerfile
├── extensions
│ ├── apm-server
│ ├── app-search
│ ├── curator
│ ├── logspout
├── kibana
│ ├── config
│ │ └── kibana.yml
│ └── Dockerfile
├── LICENSE
├── logstash
│ ├── config
│ │ └── logstash.yml
│ ├── Dockerfile
│ └── pipeline
│ └── logstash.conf
└── README.md
└── .env

1.3 docker compose

據官網介紹,docker compose 是一個方便配置多 container 的工具。之前一個容器一個容器的 run ,有好多配置要直接寫在命令列上,如果需要再次執行就要再寫一遍引數很不方便。compose 文件:docker-compsoe

注:專案根目錄下的 .env 檔案中定義了一個變數,指定了 elk 的版本,預設是 latest。可自行修改,修改後需要 build 才會生效:docker-compose build。欲知詳情請參考專案文件。

要執行 docker compose 專案,請切換到專案根目錄,然後執行 docker-compose up [service name]命令,如果不指定 service name,則啟動所有服務。至於停止執行等其他的命令,請去參考文件內容。這裡建議初次使用之前至少認真閱讀一遍文件。

2 Elastics Stack 的配置

首先進行下述配置:

  1. 修改/etc/sysctl.conf:vm.max_map_count = 262144,修改完執行 sysctl -p使之生效或者退出當前終端重進使之生效。

  2. elasticsearch.yml 配置檔案中的 xpack.license.self_generated.type 修改為 basic 來禁用 X-Pack 相關收費功能。

  3. 由於機器記憶體太小,導致實際執行中容器各種退出,我選擇將 Elastic Stack 三者的 xpack.monitoring.enabled 統統關閉。開啟 .yml,修改相應配置項為:

     xpack.monitoring.collection.enabled: false
     xpack.monitoring.enabled: false
    
  4. 特別注意,對於阿里雲伺服器,還需要配置安全組規則以開放 docker 的埠,否則外網無法訪問 docker 容器。本文新增埠規則如圖:
    image

接下來分別記錄 Elastic Stack 的配置。需要說明的是,關於這三者的配置,前面提到過,編排檔案可以極大的方便 docker 的容器配置,尤其是多容器場景,使 docker 更方便的 run。同時,每個容器在編排檔案裡的環境變數(?) enviroment 裡也可以進行此容器本身的配置。也就是說,本文要配置的 Elastic Stack,既可以找到他們各自目錄裡的 .yml 在裡面寫配置項,也可以在編排檔案裡找到各容器的 environment 屬性,在其下寫配置項。

2.1 Elasticsearch

Docker compose 編排檔案(docker-compose.yml)裡的 Elasticsearch service 定義了 Elasticsearch 的 docker 配置,以及一些 Elasticsearch 本身的執行引數。本文主要進行了 Elasticsearch logging 寫入檔案並持久化儲存到磁碟以及跨域開啟這兩點設定。

2.1.1 跨域

首先進行跨域開啟設定。為了能用 elasticsearch-head 外掛訪問 elasticsearch,elasticsearch 需要開啟跨域。

如前所述,直接在 docker-compose.yml 裡配置和在 elasticsearch.yml 裡配置這兩種方式都可以。本文直接在編排檔案裡配。找到 elasticsearch service 的 environment 屬性,在其下新增(參考 elasticsearch-head 文件):

  http.cors.enabled: "true"
  http.cors.allow-origin: "*"

因為 docker-elk 專案初始的 Elastic Stack 都有認證機制,故還需要新增:

  http.cors.allow-headers: "Content-Type,Content-Length,Authorization"
  http.cors.allow-credentials: "true"

此時,啟動 elasticsearch 容器:docker-compose up elasticsearch,瀏覽器用 elasticsearh-head 訪問 http://yourhost:9200/ 可以看到仍無法連線,開啟F12,會發現 ERROR CODE 401。這時參考 elasticsearch-head 文件中的說明,在地址後新增認證資訊:http://yourhost:9100/?auth_user=elastic&auth_password=changeme。注意,本文這裡的使用者名稱和密碼用的是預設的。

2.1.2 logging

Elasticsearch 的執行日誌記錄(logging)一般而言不需要,但本文在此次部署過程中發生了多起 elasticsearch 的報錯事件,為方便填坑學習,本文配置日誌記錄並做持久化。

Elastic Stack 執行日誌預設輸出到 console,在螢幕上可以看到,但是寫到檔案裡則需要配置日誌儲存的路徑(容器內的路徑):

path.logs: /usr/share/elasticsearch/logs

下一步是配置 log4j2.properties 這個檔案。在 elasticsearch config 下新建一個檔案,叫做log4j2.properties。其內容本文選擇從容器內拷貝過來。注意,容器內有兩個長得比較像的檔案,一個是 log4j2.properties,另一個是log4j2.file.properties。其區別是後者多了日誌寫入到檔案的配置。所以,後者的內容是我們需要拷貝的。

檔案新建好後,需要繫結到容器內。開啟 docker-compose.yml,在 elasticsearch 的 volumes 裡新增:

- type: bind
    source: ./elasticsearch/config/log4j2.properties
    target: /usr/share/elasticsearch/config/log4j2.properties
    read_only: true

當然,實現配置對映還有其他方式,本文采用上述這種方式,即通過繫結掛載檔案的方式。關於繫結掛載下文會再次提及。

此時,啟動容器,並進入容器內部,檢視對應資料夾是否有 log 檔案。如果沒有 log 檔案請逐一檢查上述配置,有時可能只是筆誤打錯了字導致。

注:每次修改配置檔案後需要重新啟動。如果配置仍不生效,手動停止容器docker stop [container id]然後手動啟動docker-compose up logstash。這裡記錄一下,docker-compose down 命令是停止並移除例項(docker-compose down -v 命令則會連同 volume 一起移除)。

關於 logging 的其他內容,參考官方文件

以上並沒有對日誌資料做持久化,這裡先不進行此步,留待下文再說。

2.2 Logstash

本文主要的工作量其實都在Logstash,故 Logstash 的配置內容還是比較多的。

關於從 sql server 匯入資料到 es,本文采用方案:https://www.cnblogs.com/baiyunchen/p/11216165.html

2.2.1 匯入資料

首先,下載 jdbc jar 包,地址:https://docs.microsoft.com/en-us/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-2017
下載後解壓到相應路徑。

然後,準備好一份 .conf 檔案,下面給出模板。

input {
  jdbc {
    jdbc_driver_library => "/usr/local/logstash-7.2.0/lib/mssql-jdbc-7.2.2/mssql-jdbc-7.2.2.jre8.jar" # 這裡請靈活應變,能找到我們上一步下載的jdbc jar包即可
    jdbc_driver_class => "com.microsoft.sqlserver.jdbc.SQLServerDriver" # 這個名字是固定的
    jdbc_connection_string => "jdbc:sqlserver: //資料庫ServerIP:1433;databaseName=資料庫名;"
    jdbc_user => "資料庫賬號"
    jdbc_password => "資料庫密碼"
    schedule => "* * * * *" # Corn 表示式,請自行百度寫法
    jdbc_default_timezone => "Asia/Shanghai"
    jdbc_page_size => "500" # 每一批傳輸的數量
    record_last_run => "true" #是否儲存狀態 
    use_column_value => "true" #設定為時true,使用定義的 tracking_column值作為:sql_last_value。設定為時false,:sql_last_value反映上次執行查詢的時間。
    tracking_column => "LastModificationTime" #配合use_column_value使用
    last_run_metadata_path => "/usr/opt/logstash/config/last_value" #記錄:sql_last_value的檔案
    lowercase_column_names => "false" #將DB中的列名自動轉換為小寫
    tracking_column_type => "timestamp" #tracking_column的資料型別,只能是numberic和timestamp
    clean_run => "false" #是否應保留先前的執行狀態,具體參考文件~~
    #statement => "SELECT * FROM 表 WITH(NOLOCK) WHERE LastModificationTime > :sql_last_value" #從DB中抓資料的SQL指令碼
    statement_filepath => "/file/path/some.sql" #檔案形式的SQL指令碼,路徑注意雙引號括起來
 }
}
output {
 elasticsearch {
    index => "your_index"
    document_id => "%{Id}"
    hosts => ["elasticsearch:9200"]
    user => "elastic"
    password => "changeme"
    ecs_compatibility => disabled
 }
}

上面的 input jdbc plugin 中 SQL 語句配置了 statement_filepath。因此對應的 some.sql 檔案也需要通過 bind-mount 繫結到容器中。

關於 input 的 jdbc plugin,可以參考文件:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-jdbc.html

這裡再給出一份正式版本的 .conf 檔案:

input {
 jdbc {
    jdbc_driver_library => "/usr/share/logstash/lib/sqljdbc_9.2/chs/mssql-jdbc-9.2.1.jre11.jar"
    jdbc_driver_class => "com.microsoft.sqlserver.jdbc.SQLServerDriver"
    jdbc_connection_string => "jdbc:sqlserver://yourip:1433;databaseName=yourDB;"
    jdbc_user => "user_name"
    jdbc_password => "your_passwd"
    schedule => "*/15 * * * *"
    jdbc_default_timezone => "Asia/Shanghai"
    jdbc_page_size => "500"
    record_last_run => "true"
    #use_column_value => "true"
    #tracking_column => "LastModificationTime"
    last_run_metadata_path => "/usr/share/logstash/config/last_value"
    lowercase_column_names => "false"
    tracking_column_type => "timestamp"
    clean_run => "false"
    statement_filepath => "/usr/share/logstash/item_warehouse.sql"
    #statement => "SELECT * FROM Table1 WITH(NOLOCK) WHERE (LastModificationTime >= :sql_last_value OR CreationTime >= :sql_last_value) AND IsDeleted=0"
 }
}
filter {
    mutate {
            rename => {"Field1" => "欄位1" "Field2" => "欄位2" }
    }
}
output {
elasticsearch {
    index => "your_index"
    document_id => "%{Id}"
    hosts => ["elasticsearch:9200"]
    user => "elastic"
    password => "changeme"
    ecs_compatibility => disabled
 }
}

這裡定時查詢以跟蹤表 Table1 的變化,依據是上一次查詢時間。第一次查詢的初始時間是'1970-01-01 08:00',具體請檢視上述 jdbc 文件。這裡的 filter 用來完成重新命名欄位名稱的工作。

此時,執行 Elasticsearch 和 Logstash,在 head 外掛上開啟可以看到相應的 index 及其 document。

2.2.2 logging

與 Elasticsearch 一樣,本文出於排除 Bug 的需要,配置 logstash 的 logging。參考:官方文件pipeline logs

log 寫入的配置與前文 Elasticsearch 一樣,需要指定 logs 資料夾的路徑。

另外,可以順便指定 log 的 level 和格式。log level 預設是 info 級別的。在 yml 中新增如下三行。

log.level: info
path.logs: /usr/share/logstash/logs
log.format: plain

logstash 同樣需要配置log4j2.properties檔案。如果不指定,則使用預設的檔案。這裡要注意,預設的檔案是 docker 版的,比正常版本的簡化了很多。這裡貼出正常版本的 logstash (version 7.12)的log4j2.properties地址:https://github.com/elastic/logstash/blob/7.12/config/log4j2.properties

新建的log4j2.properties檔案同樣需要 bind mount,步驟與前文 Elasticsearch 雷同,此處不再贅述。

以上兩點配置好後,執行 logstash,發現 logs 下會有相應檔案。


下面將 log 儲存到硬碟以持久化資料。思路是利用資料卷 volume。這裡還可以用 bind mount 繫結掛載實現資料持久化到硬碟,不過本文沒有深入探究。關於 volume 和 bind mount 的內容請看文件:volumesbind-mounts and volumes

  1. 開啟 docker-compose.yml 到檔案末尾找到 volumes,在其下新增 named volume,如下所示:

     volumes:
       elasticsearch:
       logstash_logging:
       elasticsearch_logging:
    

    在此,一併將上文 Elasticsearch 的日誌資料卷新增進去。

  2. 分別給 logstash 和 elasticsearch 配置 volumes 項,如下所示:

     - type: volume
       source: elasticsearch_logging
       target: /usr/share/elasticsearch/logs
    

    source 為剛才定義的 volumes 名字,target 為在 .yml 裡配置 path.logs 的路徑。

執行容器,然後執行docker volume lsdocker volume inspect volume_name命令檢視 elasticsearch 和 logstash 的 volume 路徑。

可以看到,在 elasticsearch logging 的目錄下有相應的檔案。而 logstash 下卻沒有。進入 logstash 容器,找到之前的 logs 資料夾,發現其為空資料夾。

這是因為,logstash 容器是以 logstash 使用者執行的,而自動生成的 volume 資料卷的儲存目錄需要 root 許可權。反觀 elasticsearch,它預設是以 root 使用者執行的。這一點在進入容器後也可以觀察到。所以,本文的解決方案是,設定 logstash 以 root 使用者啟動,在編排檔案 logstash 裡新增屬性user: root,如下所示:

...
user: root
ports:
  - "5044:5044"
  - "5000:5000/tcp"
  - "5000:5000/udp"
  - "9600:9600"
...

到此,logstash 的基本配置已經完成,下面進行附加配置:logstash multiple pipelines 的配置。
之所以配置多管道,是想把 logging 的東西展示到 kibana 裡,檢視它執行的 SQL Statement 等資訊。

在開始附加配置之前,先說一個問題。本文選擇的阿里雲例項配置比較低,容器執行過程中總是報 exited)

2.2.3 multiple pipelines

logstash 支援多 pipeline,參考:官方文件pipeline logs
1.首先,建立新的 .conf 檔案,本文選擇在 logstash 的 pipeline 資料夾下建立。本文使用的檔案如下:

######
input {
file {
    path => "/usr/share/logstash/logs/logstash-plain.log"
    #sincedb_path => "/usr/share/logstash/logs/sincedb"
    sincedb_path => "/dev/null"
    #tags => ["some-logs"]
}
}

filter {
grok {
    match => { "message" => "\[%{TIMESTAMP_ISO8601:tstamp}\]\[%{LOGLEVEL:loglevel} \]\[%{JAVACLASS:class}.*\] %{GREEDYDATA:logmessage}" }
}
date {
    match => ["tstamp", "ISO8601"]
}
mutate {
    remove_field => [ "tstamp" ]
}
}

output {
if "_grokparsefailure" not in [tags] {
elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "logstash_logs-%{+YYYY.MM.dd}"
    user => "elastic"
    password => "changeme"
    ecs_compatibility => disabled
}
    stdout {   }
}
}

本文在這裡遇到一點問題,即新建檔案的首行首字母會被“吞”掉,於是將首行作為註釋行。

2.在 logstash 的 config 資料夾下新建一個檔案叫做 pipelines.yml,具體配置如下:

# This file is where you define your pipelines. You can define multiple.
# # For more information on multiple pipelines, see the documentation:
# #   https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html
#
- pipeline.id: main
path.config: "/usr/share/logstash/pipeline/logstash.conf"

- pipeline.id: logs
path.config: "/usr/share/logstash/pipeline/logs.conf"

此處記錄一下:本文在配置的時候,將 .conf 與 .yml 檔案字尾搞混了,以致於耽誤了很多時間。

同樣地,配置檔案寫好需要掛載到容器內,給編排檔案裡的 logstash 新增此項:

- type: bind
    source: ./logstash/config/pipelines.yml
    target: /usr/share/logstash/config/pipelines.yml
    read_only: true

啟動 logstash,在 head 上可以看到 logging 的資料同步到了 es。

2.3 Kibana

Kibana的 yml 檔案配置大致如下:

---
## Default Kibana configuration from Kibana base image.
## https://github.com/elastic/kibana/blob/master/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts
#
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
xpack.monitoring.enabled: false
##
i18n.locale: "zh-CN"
## X-Pack security credentials
#
elasticsearch.username: elastic
elasticsearch.password: changeme
#elasticsearch.username: kibana-system
#elasticsearch.password: mjHPFOvECWnAmeaKhMqf

i18n.locale這一行是設定Kibana的介面語言為中文。
...
設定完畢,啟動 Kibana,訪問 host 的 5601 埠開始使用。


關於 Kibana 設定索引模式、Discover、Dashboard、KQL查詢以及角色許可權、使用者等內容,下篇文章再繼續。同時,下篇文章將會總結遇到的坑?

相關文章