Nya Candy

Nya Candy

aka. Nya Crypto FSD(Fish Stack Developer) working at @rss3 with friends, Cɾყρƚσ Adventurer. candinya.eth
misskey

我有獨特的日誌收集技巧

cover

本文同步發布於 糖菓・部落

隨著伺服器越開越多,各個伺服器上運行的容器變得越來越雜,收集日誌也變成了一件令喵煩惱的事情。平時習慣使用 docker 以方便管理,並且因為玩不明白 k8s 所以沒有構建集群,收集日誌就成了一件比較尷尬的事。使用 docker 或 docker-compose 自帶的 log 指令來查詢的話,不但要連上原伺服器,進入服務目錄,還要輸入一串長長的指令,成功之後還要在一大串輸出中尋找需要的部分,一不小心剛剛找到的內容又被新的日誌刷下去了,實在有些不夠優雅。於是就試著尋找一個能夠快速方便地收集運行日誌,並進行按需查詢和整理的方案,正好前段時間在研究 Grafana 和 Loki,加上確實找到了官方開發的 docker 日誌插件,那就準備再水一篇文章記錄一下用到的配置文件和指令,方便有需要的友友們也能快速解決類似的問題。

使用時請根據您的使用情況針對性調整相關配置文件,避免一些範例配置導致的干擾。

準備 Grafana + Loki#

準備聯合啟動文件#

Loki 可以理解為實際收集日誌的伺服器,Grafana 可以理解為從伺服器裡查詢數據的前端,兩者相互獨立又相互關聯,可以在同一局域網環境內部署,也可以分開之後使用公網進行連接。因為這邊使用的是純收集日誌的特化場景,所以就直接使用一個 docker-compose 關聯啟動了。

並且由於這個伺服器是直接單給日誌收集使用的,無需與其他服務分享 443 端口,所以也直接將 Caddy 的配置寫到了一起。

docker-compose.yml 文件如下:

version: '3.4'
services:

  loki:
    image: grafana/loki
    container_name: grafana-loki
    command: -config.file=/etc/loki/local-config.yml
    networks:
      - internal_network
    volumes:
      - ./config/loki.yml:/etc/loki/local-config.yml
      - ./data/loki:/tmp/loki

  grafana:
    depends_on:
      - grafana-db
      - loki
    image: grafana/grafana
    container_name: grafana
    networks:
      - internal_network
      - external_network
    volumes:
      - ./config/grafana.ini:/etc/grafana/grafana.ini
    restart: unless-stopped

  grafana-db:
    image: postgres:15-alpine
    restart: always
    container_name: grafana-db
    volumes:
      - ./data/db:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: grafana
      POSTGRES_PASSWORD: password
      POSTGRES_DB: grafana
      POSTGRES_INITDB_ARGS: "--encoding='UTF8' --lc-collate='C' --lc-ctype='C'"
    networks:
      - internal_network

  caddy:
    image: caddy
    restart: always
    ports:
      - "443:443"
    networks:
      - internal_network
      - external_network
    volumes:
      - ./config/Caddyfile:/etc/caddy/Caddyfile
      - ./ssl:/ssl:ro
      - ./data/caddy/data:/data
      - ./data/caddy/config:/config

networks:
  internal_network:
    internal: true
  external_network:

暴露出來的唯一端口是 Caddy 的 443 端口,並在 CDN 處開啟強制 HTTPS,以確保不會有發向 80 端口的請求。

準備各個服務的配置#

將配置文件統一歸類放置在 config 目錄下,以方便管理。

Grafana#

grafana 的配置文件 grafana.ini 如下:

[database]
type = postgres
host = grafana-db:5432
name = grafana
user = grafana
password = password

Loki#

loki 的配置文件 loki.yml 如下:

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9095

common:
  path_prefix: /tmp/loki
  storage:
    filesystem:
      chunks_directory: /tmp/loki/chunks
      rules_directory: /tmp/loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

query_range:
  results_cache:
    cache:
      embedded_cache:
        enabled: true
        max_size_mb: 100

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

analytics:
  reporting_enabled: false

# 參見 https://grafana.com/docs/loki/latest/configuration/
# 可以開啟 S3 存儲,暫時沒必要就不管了

因為不知道 alertmanager_url 應該設置成什麼,並且作為收集日誌的服務暫時不考慮告警的需求,暫時就保留了範例值不變。

需要注意的是 auth_enabled 這個配置項,這個字段的意義是通過設置不同的區域請求頭來實現多個組織共享同個 loki 服務,當開啟後需要在 HTTP 調用中使用 X-Scope-OrgID 來區分發出請求的組織,在提交數據和拉取數據時都需要帶上。而此處因為是單方使用,並不涉及分享數據相關的問題,所以關閉了這個選項。

loki 本身不帶 HTTP 授權檢驗,因而推薦將授權請求頭寫在前端反向代理程序的配置中。例如此處放在 Caddy 配置文件中,僅要求對來自公網流量的請求進行授權檢驗,當 Grafana 使用內網連接時無需校驗。

Caddy#

這裡使用了 CDN 提供商的源伺服器證書,無需讓 Caddy 申請,所以配置文件如下:

example.com {
    tls /ssl/example.com/cert.pem /ssl/example.com/key.pem
    reverse_proxy grafana:3000
}
loki.example.com {
    tls /ssl/example.com/cert.pem /ssl/example.com/key.pem
    reverse_proxy loki:3100
    basicauth * {
        user $2a$14$QZIF1JLM9lFPjAl6PQzBDeANmB4Ssufc0qUnP9nI7QYnv.QevEzyi
    }
}
loki-grpc.example.com {
    tls /ssl/example.com/cert.pem /ssl/example.com/key.pem
    reverse_proxy loki:9095
    basicauth * {
        user $2a$14$QZIF1JLM9lFPjAl6PQzBDeANmB4Ssufc0qUnP9nI7QYnv.QevEzyi
    }
}

其中授權部分使用的是 BasicAuth,即用戶名 + 密碼的方式;密碼並非明文存儲,而是由 caddy 的 hash-password 功能預先計算得出,以盡可能提升安全性。

/srv # caddy hash-password
Enter password: 
Confirm password: 
$2a$14$QZIF1JLM9lFPjAl6PQzBDeANmB4Ssufc0qUnP9nI7QYnv.QevEzyi
/srv #

在配置完成後,還需要將對應的 SSL 證書放到指定位置,以避免因沒有證書文件導致的報錯。

當全部配置完成後,應該就可以使用 docker-compose up -d 命令啟動啦。

管理 Loki 數據目錄的權限#

容器中的 Loki 不是以 root 用戶啟動的,所以 Docker 設置的 root 權限會導致 Loki 存取被拒絕,您需要手動執行形如以下的命令,來將 Loki 的數據目錄權限開放給容器使用:

chown -R 10001:10001 ./data/loki/

修改完成後重啟 Loki 容器即可。

關聯 Loki 作為 Grafana 的數據來源#

進入部署好的 Grafana,使用默認帳號密碼 admin 登錄,修改密碼之後,就可以開始添加數據源了。

如果根據上述的 docker-compose 配置方案,則只需選擇 Loki,在 HTTP 的 URL 處輸入 http://loki:3100,即可保存並測試了。此時 Loki 因為沒有任何日誌,會報出一個查不到 label 的錯誤提示,不用擔心,等到之後出現了日誌就可以了。

準備伺服器#

因為使用的是 docker 環境,所以具體的指令此處就不再贅述了,直接從安裝 loki 日誌插件開始。

此處參照的是 Docker Driver Client 給出的教程,請注意相關的內容可能會更新。

安裝 loki-docker-driver 插件#

為能將數據發送給 loki,需要一個插件來將 Docker 的日誌轉化為帶有詳細標籤信息的數據,並使用 Loki 兼容的接口發送給上面部署的伺服器。使用這條指令可以安裝這個插件:

docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions

安裝完成後可以通過 docker plugin ls 查詢 docker 安裝的插件來進行檢查。

如果後續有升級需要,可以參照原鏈接中給出的指令進行升級:
docker plugin disable loki --force
docker plugin upgrade loki grafana/loki-docker-driver:latest --grant-all-permissions
docker plugin enable loki
systemctl restart docker

然後就可以對插件進行具體的配置了。因為我需要收集的是宿主機上所有的 docker 日誌,並且無需區分不同的組織,所以配置 /etc/docker/daemon.json 文件,寫入以下內容:

{
    "log-driver": "loki",
    "log-opts": {
        "loki-url": "https://user:[email protected]/loki/api/v1/push"
    }
}

配置完成後,需要重啟 docker 進程,以使更改生效。

需要注意的是,此時並不會對所有的容器都實時生效,已經創建的容器的日誌系統已經被固定,只有對新創建的容器才會有效。如果您非常急需日誌收集的話,您可以嘗試使用 docker-compose up -d --force-recreate 來重建所有的容器。

在 Grafana 上查詢#

當配置完成,且新創建的容器開始工作並產生日誌後,應該就能在 Grafana 中查詢到。因為只是單方使用,並不涉及詳細可視化的需求,所以沒有配置 Dashboard,而是直接進入 Explore 功能進行互動式查詢。

當日誌出現後,即可通過 label 對來源進行篩選。這個插件會提供 host compose_project compose_service container_name source filename 六個篩選標籤,其中:

  • host 指的是發出日誌的宿主機主機名,
  • compose_project compose_service 或是 container_name 可用於定位容器,
  • source 表示日誌來源是一般輸出 stdout 還是錯誤日誌 stderr
  • filename 指的是日誌的原始文件(通常情況下並不重要),

利用這些標籤配合 Grafana 的時間控制,就能快速查詢某一具體容器的運行日誌。再也沒有日誌分散而繁多的煩惱了!

篩選出來的日誌

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。