Linux 日誌管理指南

Jason Skowronski, Amy Echeverri, Sadequl Hussain發表於2015-09-03

管理日誌的一個最好做法是將你的日誌集中或整合到一個地方,特別是在你有許多伺服器或多層級架構時。我們將告訴你為什麼這是一個好主意,然後給出如何更容易的做這件事的一些小技巧。

Linux 日誌管理指南

集中管理日誌的好處

如果你有很多伺服器,檢視某個日誌檔案可能會很麻煩。現代的網站和服務經常包括許多伺服器層級、分散式的負載均衡器,等等。找到正確的日誌將花費很長時間,甚至要花更長時間在登入伺服器的相關問題上。沒什麼比發現你找的資訊沒有被儲存下來更沮喪的了,或者本該保留的日誌檔案正好在重啟後丟失了。

集中你的日誌使它們查詢更快速,可以幫助你更快速的解決產品問題。你不用猜測那個伺服器存在問題,因為所有的日誌在同一個地方。此外,你可以使用更強大的工具去分析它們,包括日誌管理解決方案。一些解決方案能轉換純文字日誌為一些欄位,更容易查詢和分析。

集中你的日誌也可以使它們更易於管理:

  • 它們更安全,當它們備份歸檔到一個單獨區域時會有意無意地丟失。如果你的伺服器當機或者無響應,你可以使用集中的日誌去除錯問題。
  • 你不用擔心ssh或者低效的grep命令在陷入困境的系統上需要更多的資源。
  • 你不用擔心磁碟佔滿,這個能讓你的伺服器當機。
  • 你能保持你的產品伺服器的安全性,只是為了檢視日誌無需給你所有團隊登入許可權。給你的團隊從日誌集中區域訪問日誌許可權更安全。

隨著集中日誌管理,你仍需處理由於網路聯通性不好或者耗盡大量網路頻寬從而導致不能傳輸日誌到中心區域的風險。在下面的章節我們將要討論如何聰明的解決這些問題。

流行的日誌歸集工具

在 Linux 上最常見的日誌歸集是通過使用 syslog 守護程式或者日誌代理。syslog 守護程式支援本地日誌的採集,然後通過syslog 協議傳輸日誌到中心伺服器。你可以使用很多流行的守護程式來歸集你的日誌檔案:

  • rsyslog 是一個輕量後臺程式,在大多數 Linux 分支上已經安裝。
  • syslog-ng 是第二流行的 Linux 系統日誌後臺程式。
  • logstash 是一個重量級的代理,它可以做更多高階加工和分析。
  • fluentd 是另一個具有高階處理能力的代理。

Rsyslog 是集中日誌資料最流行的後臺程式,因為它在大多數 Linux 分支上是被預設安裝的。你不用下載或安裝它,並且它是輕量的,所以不需要佔用你太多的系統資源。

如果你需要更多先進的過濾或者自定義分析功能,如果你不在乎額外的系統負載,Logstash 是另一個最流行的選擇。

配置 rsyslog.conf

既然 rsyslog 是最廣泛使用的系統日誌程式,我們將展示如何配置它為日誌中心。它的全域性配置檔案位於 /etc/rsyslog.conf。它載入模組,設定全域性指令,和包含位於目錄 /etc/rsyslog.d 中的應用的特有的配置。目錄中包含的 /etc/rsyslog.d/50-default.conf 指示 rsyslog 將系統日誌寫到檔案。在 rsyslog 文件中你可以閱讀更多相關配置。

rsyslog 配置語言是是RainerScript。你可以給日誌指定輸入,就像將它們輸出到另外一個位置一樣。rsyslog 已經配置標準輸入預設是 syslog ,所以你通常只需增加一個輸出到你的日誌伺服器。這裡有一個 rsyslog 輸出到一個外部伺服器的配置例子。在本例中,BEBOP 是一個伺服器的主機名,所以你應該替換為你的自己的伺服器名。

action(type="omfwd" protocol="tcp" target="BEBOP" port="514")

你可以傳送你的日誌到一個有足夠的儲存容量的日誌伺服器來儲存,提供查詢,備份和分析。如果你儲存日誌到檔案系統,那麼你應該建立日誌輪轉來防止你的磁碟爆滿。

作為一種選擇,你可以傳送這些日誌到一個日誌管理方案。如果你的解決方案是安裝在本地你可以傳送到系統文件中指定的本地主機和埠。如果你使用基於雲提供商,你將傳送它們到你的提供商特定的主機名和埠。

日誌目錄

你可以歸集一個目錄或者匹配一個萬用字元模式的所有檔案。nxlog 和 syslog-ng 程式支援目錄和萬用字元(*)。

常見的 rsyslog 不能直接監控目錄。作為一種解決辦法,你可以設定一個定時任務去監控這個目錄的新檔案,然後配置 rsyslog 來傳送這些檔案到目的地,比如你的日誌管理系統。舉個例子,日誌管理提供商 Loggly 有一個開源版本的目錄監控指令碼

哪個協議: UDP、TCP 或 RELP?

當你使用網路傳輸資料時,有三個主流協議可以選擇。UDP 在你自己的區域網是最常用的,TCP 用在網際網路。如果你不能失去(任何)日誌,就要使用更高階的 RELP 協議。

UDP 傳送一個資料包,那只是一個單一的資訊包。它是一個只外傳的協議,所以它不會傳送給你回執(ACK)。它只嘗試傳送包。當網路擁堵時,UDP 通常會巧妙的降級或者丟棄日誌。它通常使用在類似區域網的可靠網路。

TCP 通過多個包和返回確認傳送流式資訊。TCP 會多次嘗試傳送資料包,但是受限於 TCP 快取的大小。這是在網際網路上傳送送日誌最常用的協議。

RELP 是這三個協議中最可靠的,但是它是為 rsyslog 建立的,而且很少有行業採用。它在應用層接收資料,如果有錯誤就會重發。請確認你的日誌接受位置也支援這個協議。

用磁碟輔助佇列可靠的傳送

如果 rsyslog 在儲存日誌時遭遇錯誤,例如一個不可用網路連線,它能將日誌排隊直到連線還原。佇列日誌預設被儲存在記憶體裡。無論如何,記憶體是有限的並且如果問題仍然存在,日誌會超出記憶體容量。

警告:如果你只儲存日誌到記憶體,你可能會失去資料。

rsyslog 能在記憶體被佔滿時將日誌佇列放到磁碟。磁碟輔助佇列使日誌的傳輸更可靠。這裡有一個例子如何配置rsyslog 的磁碟輔助佇列:

$WorkDirectory /var/spool/rsyslog # 暫存檔案(spool)放置位置
$ActionQueueFileName fwdRule1     # 暫存檔案的唯一名字字首
$ActionQueueMaxDiskSpace 1g       # 1gb 空間限制(儘可能大)
$ActionQueueSaveOnShutdown on     # 關機時儲存日誌到磁碟
$ActionQueueType LinkedList       # 非同步執行
$ActionResumeRetryCount -1        # 如果主機當機,不斷重試

使用 TLS 加密日誌

如果你擔心你的資料的安全性和隱私性,你應該考慮加密你的日誌。如果你使用純文字在網際網路傳輸日誌,嗅探器和中間人可以讀到你的日誌。如果日誌包含私人資訊、敏感的身份資料或者政府管制資料,你應該加密你的日誌。rsyslog 程式能使用 TLS 協議加密你的日誌保證你的資料更安全。

建立 TLS 加密,你應該做如下任務:

  1. 生成一個證照授權(CA)。在 /contrib/gnutls 有一些證照例子,可以用來測試,但是你需要為產品環境建立自己的證照。如果你正在使用一個日誌管理服務,它會給你一個證照。
  2. 為你的伺服器生成一個數字證照使它能啟用 SSL 操作,或者使用你自己的日誌管理服務提供商的一個數字證照。
  3. 配置你的 rsyslog 程式來傳送 TLS 加密資料到你的日誌管理系統。

這有一個 rsyslog 配置 TLS 加密的例子。替換 CERT 和 DOMAIN_NAME 為你自己的伺服器配置。

$DefaultNetstreamDriverCAFile /etc/rsyslog.d/keys/ca.d/CERT.crt
$ActionSendStreamDriver gtls
$ActionSendStreamDriverMode 1
$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer *.DOMAIN_NAME.com

應用日誌的最佳管理方法

除 Linux 預設建立的日誌之外,歸集重要的應用日誌也是一個好主意。幾乎所有基於 Linux 的伺服器應用都把它們的狀態資訊寫入到獨立、專門的日誌檔案中。這包括資料庫產品,像 PostgreSQL 或者 MySQL,網站伺服器,像 Nginx 或者 Apache,防火牆,列印和檔案共享服務,目錄和 DNS 服務等等。

管理員安裝一個應用後要做的第一件事是配置它。Linux 應用程式典型的有一個放在 /etc 目錄裡 .conf 檔案。它也可能在其它地方,但是那是大家找配置檔案首先會看的地方。

根據應用程式有多複雜多龐大,可配置引數的數量可能會很少或者上百行。如前所述,大多數應用程式可能會在某種日誌檔案寫它們的狀態:配置檔案是定義日誌設定和其它東西的地方。

如果你不確定它在哪,你可以使用locate命令去找到它:

[root@localhost ~]# locate postgresql.conf
/usr/pgsql-9.4/share/postgresql.conf.sample
/var/lib/pgsql/9.4/data/postgresql.conf

設定一個日誌檔案的標準位置

Linux 系統一般儲存它們的日誌檔案在 /var/log 目錄下。一般是這樣,但是需要檢查一下應用是否儲存它們在 /var/log 下的特定目錄。如果是,很好,如果不是,你也許想在 /var/log 下建立一個專用目錄?為什麼?因為其它程式也在 /var/log 下儲存它們的日誌檔案,如果你的應用儲存超過一個日誌檔案 – 也許每天一個或者每次重啟一個 – 在這麼大的目錄也許有點難於搜尋找到你想要的檔案。

如果在你網路裡你有執行多於一個的應用例項,這個方法依然便利。想想這樣的情景,你也許有一打 web 伺服器在你的網路執行。當排查任何一個機器的問題時,你就很容易知道確切的位置。

使用一個標準的檔名

給你的應用最新的日誌使用一個標準的檔名。這使一些事變得容易,因為你可以監控和追蹤一個單獨的檔案。很多應用程式在它們的日誌檔案上追加一種時間戳。它讓 rsyslog 更難於找到最新的檔案和設定檔案監控。一個更好的方法是使用日誌輪轉給老的日誌檔案增加時間。這樣更易去歸檔和歷史查詢。

追加日誌檔案

日誌檔案會在每個應用程式重啟後被覆蓋嗎?如果這樣,我們建議關掉它。每次重啟 app 後應該去追加日誌檔案。這樣,你就可以追溯重啟前最後的日誌。

日誌檔案追加 vs. 輪轉

要是應用程式每次重啟後寫一個新日誌檔案,如何儲存當前日誌?追加到一個單獨的、巨大的檔案?Linux 系統並不以頻繁重啟或者崩潰而出名:應用程式可以執行很長時間甚至不間歇,但是也會使日誌檔案非常大。如果你查詢分析上週發生連線錯誤的原因,你可能無疑的要在成千上萬行裡搜尋。

我們建議你配置應用每天半晚輪轉(rotate)它的日誌檔案。

為什麼?首先它將變得可管理。找一個帶有特定日期的檔名比遍歷一個檔案中指定日期的條目更容易。檔案也小的多:你不用考慮當你開啟一個日誌檔案時 vi 僵住。第二,如果你正傳送日誌到另一個位置 – 也許每晚備份任務拷貝到歸集日誌伺服器 – 這樣不會消耗你的網路頻寬。最後第三點,這樣幫助你做日誌保留。如果你想剔除舊的日誌記錄,這樣刪除超過指定日期的檔案比用一個應用解析一個大檔案更容易。

日誌檔案的保留

你保留你的日誌檔案多長時間?這絕對可以歸結為業務需求。你可能被要求保持一個星期的日誌資訊,或者管理要求保持一年的資料。無論如何,日誌需要在一個時刻或其它情況下從伺服器刪除。

在我們看來,除非必要,只線上保持最近一個月的日誌檔案,並拷貝它們到第二個地方如日誌伺服器。任何比這更舊的日誌可以被轉到一個單獨的介質上。例如,如果你在 AWS 上,你的舊日誌可以被拷貝到 Glacier。

給日誌單獨的磁碟分割槽

更好的,Linux 通常建議掛載到 /var 目錄到一個單獨的檔案系統。這是因為這個目錄的高 I/O。我們推薦掛載 /var/log 目錄到一個單獨的磁碟系統下。這樣可以節省與主要的應用資料的 I/O 競爭。另外,如果一些日誌檔案變的太多,或者一個檔案變的太大,不會佔滿整個磁碟。

日誌條目

每個日誌條目中應該捕獲什麼資訊?

這依賴於你想用日誌來做什麼。你只想用它來排除故障,或者你想捕獲所有發生的事?這是一個捕獲每個使用者在執行什麼或檢視什麼的規則條件嗎?

如果你正用日誌做錯誤排查的目的,那麼只儲存錯誤,報警或者致命資訊。沒有理由去捕獲除錯資訊,例如,應用也許預設記錄了除錯資訊或者另一個管理員也許為了故障排查而開啟了除錯資訊,但是你應該關閉它,因為它肯定會很快的填滿空間。在最低限度上,捕獲日期、時間、客戶端應用名、來源 ip 或者客戶端主機名、執行的動作和資訊本身。

一個 PostgreSQL 的例項

作為一個例子,讓我們看看 vanilla PostgreSQL 9.4 安裝的主配置檔案。它叫做 postgresql.conf,與其它Linux 系統中的配置檔案不同,它不儲存在 /etc 目錄下。下列的程式碼段,我們可以在我們的 Centos 7 伺服器的 /var/lib/pgsql 目錄下找到它:

root@localhost ~]# vi /var/lib/pgsql/9.4/data/postgresql.conf
... 
#------------------------------------------------------------------------------
# ERROR REPORTING AND LOGGING
#------------------------------------------------------------------------------
# - Where to Log -
log_destination = 'stderr'      
      # Valid values are combinations of
      # stderr, csvlog, syslog, and eventlog,
      # depending on platform. csvlog
      # requires logging_collector to be on.
# This is used when logging to stderr:
logging_collector = on          
      # Enable capturing of stderr and csvlog
      # into log files. Required to be on for
      # csvlogs.
      # (change requires restart)
# These are only used if logging_collector is on:
log_directory = 'pg_log'       
      # directory where log files are written,
      # can be absolute or relative to PGDATA
log_filename = 'postgresql-%a.log'    # log file name pattern,
     # can include strftime() escapes
# log_file_mode = 0600           .
     # creation mode for log files,
     # begin with 0 to use octal notation
log_truncate_on_rotation = on   # If on, an existing log file with the
     # same name as the new log file will be
     # truncated rather than appended to.
     # But such truncation only occurs on
     # time-driven rotation, not on restarts
     # or size-driven rotation. Default is
     # off, meaning append to existing files
     # in all cases.
log_rotation_age = 1d           
     # Automatic rotation of logfiles will happen after that time. 0 disables.
log_rotation_size = 0           # Automatic rotation of logfiles will happen after that much log output. 0 disables.
# These are relevant when logging to syslog:
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
# This is only relevant when logging to eventlog (win32):
#event_source = 'PostgreSQL'
# - When to Log -
#client_min_messages = notice   # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# log
# notice
# warning
# error
#log_min_messages = warning     # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic
#log_min_error_statement = error    # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic (effectively off)
#log_min_duration_statement = -1     # -1 is disabled, 0 logs all statements
# and their durations, > 0 logs only
# statements running at least this number
# of milliseconds
# - What to Log 
#debug_print_parse = off
#debug_print_rewritten = off
#debug_print_plan = off
#debug_pretty_print = on
#log_checkpoints = off
#log_connections = off
#log_disconnections = off
#log_duration = off
#log_error_verbosity = default    
# terse, default, or verbose messages
#log_hostname = off
log_line_prefix = '< %m >'          # special values:
# %a = application name
# %u = user name
# %d = database name
# %r = remote host and port
# %h = remote host
# %p = process ID
# %t = timestamp without milliseconds
# %m = timestamp with milliseconds
# %i = command tag
# %e = SQL state
# %c = session ID
# %l = session line number
# %s = session start timestamp
# %v = virtual transaction ID
# %x = transaction ID (0 if none)
# %q = stop here in non-session
# processes
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off               # log lock waits >= deadlock_timeout
#log_statement = 'none'             # none, ddl, mod, all
#log_temp_files = -1                # log temporary files equal or larger
# than the specified size in kilobytes;5# -1 disables, 0 logs all temp files5
log_timezone = 'Australia/ACT'

雖然大多數引數被加上了註釋,它們使用了預設值。我們可以看見日誌檔案目錄是 pglog(logdirectory 引數,在 /var/lib/pgsql/9.4/data/ 下的子目錄),檔名應該以 postgresql 開頭(logfilename引數),檔案每天輪轉一次(logrotationage 引數)然後每行日誌記錄以時間戳開頭(loglineprefix引數)。特別值得說明的是 logline_prefix 引數:全部的資訊你都可以包含在這。

看 /var/lib/pgsql/9.4/data/pg_log 目錄下展現給我們這些檔案:

[root@localhost ~]# ls -l /var/lib/pgsql/9.4/data/pg_log
total 20
-rw-------. 1 postgres postgres 1212 May 1 20:11 postgresql-Fri.log
-rw-------. 1 postgres postgres 243 Feb 9 21:49 postgresql-Mon.log
-rw-------. 1 postgres postgres 1138 Feb 7 11:08 postgresql-Sat.log
-rw-------. 1 postgres postgres 1203 Feb 26 21:32 postgresql-Thu.log
-rw-------. 1 postgres postgres 326 Feb 10 01:20 postgresql-Tue.log

所以日誌檔名只有星期命名的標籤。我們可以改變它。如何做?在 postgresql.conf 配置 log_filename 引數。

檢視一個日誌內容,它的條目僅以日期時間開頭:

[root@localhost ~]# cat /var/lib/pgsql/9.4/data/pg_log/postgresql-Fri.log
...
< 2015-02-27 01:21:27.020 EST >LOG: received fast shutdown request
< 2015-02-27 01:21:27.025 EST >LOG: aborting any active transactions
< 2015-02-27 01:21:27.026 EST >LOG: autovacuum launcher shutting down
< 2015-02-27 01:21:27.036 EST >LOG: shutting down
< 2015-02-27 01:21:27.211 EST >LOG: database system is shut down

歸集應用的日誌

使用 imfile 監控日誌

習慣上,應用通常記錄它們資料在檔案裡。檔案容易在一個機器上尋找,但是多臺伺服器上就不是很恰當了。你可以設定日誌檔案監控,然後當新的日誌被新增到檔案尾部後就傳送事件到一個集中伺服器。在 /etc/rsyslog.d/ 裡建立一個新的配置檔案然後增加一個配置檔案,然後輸入如下:

$ModLoad imfile
$InputFilePollInterval 10
$PrivDropToGroup adm
# Input for FILE1
$InputFileName /FILE1
$InputFileTag APPNAME1
$InputFileStateFile stat-APPNAME1 #this must be unique for each file being polled
$InputFileSeverity info
$InputFilePersistStateInterval 20000
$InputRunFileMonitor

替換 FILE1 和 APPNAME1 為你自己的檔名和應用名稱。rsyslog 將傳送它到你配置的輸出目標中。

本地套接字日誌與 imuxsock

套接字類似 UNIX 檔案控制程式碼,所不同的是套接字內容是由 syslog 守護程式讀取到記憶體中,然後傳送到目的地。不需要寫入檔案。作為一個例子,logger 命令傳送它的日誌到這個 UNIX 套接字。

如果你的伺服器 I/O 有限或者你不需要本地檔案日誌,這個方法可以使系統資源有效利用。這個方法缺點是套接字有佇列大小的限制。如果你的 syslog 守護程式宕掉或者不能保持執行,然後你可能會丟失日誌資料。

rsyslog 程式將預設從 /dev/log 套接字中讀取,但是你需要使用如下命令來讓 imuxsock 輸入模組 啟用它:

$ModLoad imuxsock

UDP 日誌與 imupd

一些應用程式使用 UDP 格式輸出日誌資料,這是在網路上或者本地傳輸日誌檔案的標準 syslog 協議。你的 syslog 守護程式接受這些日誌,然後處理它們或者用不同的格式傳輸它們。備選的,你可以傳送日誌到你的日誌伺服器或者到一個日誌管理方案中。

使用如下命令配置 rsyslog 通過 UDP 來接收標準埠 514 的 syslog 資料:

$ModLoad imudp
$UDPServerRun 514

用 logrotate 管理日誌

日誌輪轉是當日志到達指定的時期時自動歸檔日誌檔案的方法。如果不介入,日誌檔案一直增長,會用盡磁碟空間。最後它們將破壞你的機器。

logrotate 工具能隨著日誌的日期擷取你的日誌,騰出空間。你的新日誌檔案保持該檔名。你的舊日誌檔案被重新命名加上字尾數字。每次 logrotate 工具執行,就會建立一個新檔案,然後現存的檔案被逐一重新命名。你來決定何時舊檔案被刪除或歸檔的閾值。

當 logrotate 拷貝一個檔案,新的檔案會有一個新的 inode,這會妨礙 rsyslog 監控新檔案。你可以通過增加copytruncate 引數到你的 logrotate 定時任務來緩解這個問題。這個引數會拷貝現有的日誌檔案內容到新檔案然後從現有檔案截短這些內容。因為日誌檔案還是同一個,所以 inode 不會改變;但它的內容是一個新檔案。

logrotate 工具使用的主配置檔案是 /etc/logrotate.conf,應用特有設定在 /etc/logrotate.d/ 目錄下。DigitalOcean 有一個詳細的 logrotate 教程

管理很多伺服器的配置

當你只有很少的伺服器,你可以登入上去手動配置。一旦你有幾打或者更多伺服器,你可以利用工具的優勢使這變得更容易和更可擴充套件。基本上,所有的事情就是拷貝你的 rsyslog 配置到每個伺服器,然後重啟 rsyslog 使更改生效。

pssh

這個工具可以讓你在很多伺服器上並行的執行一個 ssh 命令。使用 pssh 部署僅用於少量伺服器。如果你其中一個伺服器失敗,然後你必須 ssh 到失敗的伺服器,然後手動部署。如果你有很多伺服器失敗,那麼手動部署它們會話費很長時間。

Puppet/Chef

Puppet 和 Chef 是兩個不同的工具,它們能在你的網路按你規定的標準自動的配置所有伺服器。它們的報表工具可以使你瞭解錯誤情況,然後定期重新同步。Puppet 和 Chef 都有一些狂熱的支持者。如果你不確定那個更適合你的部署配置管理,你可以拜讀一下 InfoWorld 上這兩個工具的對比

一些廠商也提供一些配置 rsyslog 的模組或者方法。這有一個 Loggly 上 Puppet 模組的例子。它提供給 rsyslog 一個類,你可以新增一個標識令牌:

node 'my_server_node.example.net' {
  # Send syslog events to Loggly
  class { 'loggly::rsyslog':
    customer_token => 'de7b5ccd-04de-4dc4-fbc9-501393600000',
  }
}

Docker

Docker 使用容器去執行應用,不依賴於底層服務。所有東西都執行在內部的容器,你可以把它想象為一個功能單元。ZDNet 有一篇關於在你的資料中心使用 Docker 的深入文章。

這裡有很多方式從 Docker 容器記錄日誌,包括連結到一個日誌容器,記錄到一個共享卷,或者直接在容器裡新增一個 sysllog 代理。其中最流行的日誌容器叫做 logspout。

供應商的指令碼或代理

大多數日誌管理方案提供一些指令碼或者代理,可以從一個或更多伺服器相對容易地傳送資料。重量級代理會耗盡額外的系統資源。一些供應商像 Loggly 提供配置指令碼,來使用現存的 syslog 守護程式更輕鬆。這有一個 Loggly 上的例子指令碼,它能執行在任意數量的伺服器上。

相關文章