在這個雲端運算盛行的時代,擁有一個完善的監控系統對於維護專案的穩定性至關重要。玄貓過去幾年在建置個人專案的過程中,深刻體會到可觀察性(Observability)的重要性。今天就來分享如何為個人專案開發一個專業級的監控系統。

現有環境架構

基礎設施環境

我們的專案執行在一台配備 4 核心處理器和 4GB 記憶體的虛擬私人伺服器(VPS)上,採用 Docker 容器化佈署。主要的系統架構包含:

  • 反向代理伺服器(Reverse Proxy):使用 Nginx 代理配合 ACME Companion,實作自動化的 SSL 憑證管理
  • 內容管理系統:採用 Ghost 平台處理部落格相關服務
  • 應用程式框架:使用 Laravel 框架開發管理後台與前端介面
  • 後端服務:採用 Go 語言開發高效能的後端應用程式介面

監控需求分析

在建置監控系統時,玄貓特別關注以下四個核心導向:

  1. 效能指標(Metrics)監控
  • 系統資源使用率追蹤
  • 應用程式效能資料收集
  • 容器層級的資源監控
  1. 日誌管理(Logging)
  • 集中式日誌收集架構
  • 容器執行日誌追蹤
  • 應用程式錯誤日誌分析
  1. 告警機制(Alerting)
  • 系統異常即時通知
  • 效能瓶頸預警
  • 服務可用性監控
  1. 分散式追蹤(Tracing)
  • 服務間呼叫鏈路追蹤
  • 效能瓶頸識別
  • 問題定位與分析

核心監控工具鏈

為了實作上述監控需求,玄貓精心挑選了以下工具組合:

指標收集與儲存

VictoriaMetrics 作為核心的時序資料函式庫較於 Prometheus,它具有:

  • 更高的寫入效能
  • 更低的資源消耗
  • 完整的 Prometheus 相容性
  • 優異的壓縮比率

追蹤系統

同時整合了兩套追蹤解決方案:

  1. Jaeger
  • 業界標準的分散式追蹤系統
  • 豐富的追蹤資料視覺化
  • 完整的 OpenTelemetry 支援
  1. Tempo
  • 高效能的追蹤資料處理
  • 優異的擴充套件性
  • 與 Grafana 生態系統整合

視覺化與日誌管理

  • Grafana:強大的資料視覺化平台,支援多種資料來源整合
  • Loki:輕量級的日誌聚合系統,能有效替代傳統的 ELK Stack,具有更好的資源使用效率

在實際佈署過程中,玄貓特別注意這些工具的資源消耗,確保監控系統本身不會對主要應用程式造成太大負擔。這個監控架構不僅滿足了個人專案的需求,同時也為未來的擴充套件預留了彈性。

在多年的技術架構設計經驗中,玄貓深刻體會到可觀測性對於現代化系統的重要性。今天,我將分享如何建置一個完整的監控系統,整合多項強大的開放原始碼工具,實作全方位的系統監控。

監控系統架構設計

在設計這套監控系統時,我特別注重三個關鍵導向:

  1. 資料收集的全面性
  2. 系統的可擴充套件性
  3. 監控工具的整合性

核心元件選擇與設定

資料收集層

在資料收集層面,我選擇了 Vector 作為主要的收集工具。Vector 的強大之處在於:

  1. 支援多種資料來源的統一收集
  2. 內建 OpenTelemetry 協定支援
  3. 優異的效能表現

追蹤系統的雙軌策略

在追蹤系統的選擇上,我採用了 Jaeger 與 Tempo 雙軌平行的策略。這個決定根據以下考量:

  • Jaeger 作為業界標準,具備廣泛的工具支援
  • Tempo 提供更高效的水平擴充套件能力,特別適合大規模佈署

Docker Compose 設定詳解

以下是完整的 Docker Compose 設定,我們逐一解析其中的重要設定:

services:
  vector:
    image: timberio/vector:latest-alpine
    volumes:
      - ./configs/vector/vector.yaml:/etc/vector/vector.yaml:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - loki
      - victoriametrics
    networks:
      - monitoring

  otel-collector:
    image: otel/opentelemetry-collector:latest
    volumes:
      - ./configs/otel-collector/otel-collector-config.yml:/etc/otel-collector/otel-collector-config.yaml
    command: ["--config=/etc/otel-collector/otel-collector-config.yaml"]
    networks:
      - monitoring

  tempo:
    image: grafana/tempo:latest
    volumes:
      - ./configs/tempo/tempo-config.yaml:/etc/tempo/tempo-config.yaml
    command: ["--config.file=/etc/tempo/tempo-config.yaml"]
    restart: unless-stopped
    networks:
      - monitoring

設定解密

讓我們深入理解每個元件的設定:

  1. Vector 設定

    • 使用 Alpine 版本映像檔以減少容器大小
    • 掛載兩個關鍵卷:設定檔與 Docker socket
    • 建立對 Loki 與 VictoriaMetrics 的相依性
  2. OpenTelemetry Collector

    • 作為集中式的遙測資料收集器
    • 透過設定檔案進行自定義設定
    • 與監控網路整合
  3. Tempo 追蹤系統

    • 採用官方映像檔
    • 使用外部設定檔案
    • 設定自動重啟策略
  loki:
    image: grafana/loki:latest
    command: -config.file=/etc/loki/loki-config.yaml
    volumes:
      - ./configs/loki/loki-config.yaml:/etc/loki/loki-config.yaml
      - loki-data:/loki
    networks:
      - monitoring

  victoriametrics:
    image: victoriametrics/victoria-metrics:latest
    volumes:
      - victoriametrics-data:/storage
      - ./configs/victoria/victoriametrics-scrape.yml:/etc/victoria/prometheus.yml:ro
    command:
      - '--retentionPeriod=3'
      - '--promscrape.config=/etc/victoria/prometheus.yml'
    networks:
      - monitoring

日誌與指標儲存設定解密

  1. Loki 設定

    • 提供高效的日誌儲存功能
    • 使用持久化卷儲存日誌資料
    • 透過外部設定檔案自定義行為
  2. VictoriaMetrics 設定

    • 設定三天的資料保留期
    • 使用 Prometheus 相容的抓取設定
    • 採用獨立的資料儲存卷
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    depends_on:
      - loki
      - victoriametrics
      - jaeger
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    networks:
      - monitoring

  jaeger:
    image: jaegertracing/all-in-one:latest
    environment:
      - COLLECTOR_ZIPKIN_HOST_PORT=:9411
      - PROMETHEUS_SERVER_URL=http://victoriametrics:8428
    volumes:
      - jaeger-data:/data
    networks:
      - monitoring

視覺化與追蹤系統設定解密

  1. Grafana 設定

    • 提供統一的視覺化介面
    • 設定預設管理員密碼
    • 停用使用者註冊功能
    • 建立與其他服務的相依關係
  2. Jaeger 設定

    • 支援 Zipkin 協定
    • 整合 VictoriaMetrics 作為指標儲存
    • 使用持久化儲存確保資料儲存
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    networks:
      - monitoring

volumes:
  loki-data:
  victoriametrics-data:
  grafana-data:
  jaeger-data:

networks:
  monitoring:
    external: true

系統監控與網路設定解密

  1. cAdvisor 設定

    • 掛載必要的系統目錄以收集容器指標
    • 唯讀方式存取系統資源
    • 整合進監控網路
  2. 持久化儲存與網路設定

    • 為每個需要持久化的服務建立獨立卷
    • 使用外部網路確保監控系統的獨立性

在建置這套監控系統時,玄貓特別注重各元件間的協同運作。透過精心的設定和整合,我們建立了一個強大與靈活的監控架構,能夠有效支援各種規模的應用系統。這套設定不僅提供了完整的可觀測性,還保留了未來擴充套件的彈性。

建置完整的監控系統是一項複雜的工程,但透過適當的工具選擇和設定,我們可以建立一個既強大又易於維護的監控環境。這套設定方案已在多個專案中證明其價值,希望這些實戰經驗能幫助更多團隊建立更好的監控系統。

在建置現代化的容器監控系統時,良好的日誌管理、追蹤和分析能力是不可或缺的。玄貓今天要分享如何設定 Vector、Loki 和 Tempo 這三個重要元件,開發一個完整的監控解決方案。

Vector 日誌收集器設定

首先讓我們來看 Vector 的設定。Vector 是一個高效能的日誌收集和轉發工具,能夠從多個來源收集日誌並進行處理。主要設定檔案 vector.yaml 如下:

# 來源設定:從 Docker 收集日誌
sources:
  docker_logs:
    type: "docker_logs"

# 轉換設定:增加服務標籤
transforms:
  parse_logs:
    type: "remap"
    inputs:
      - "docker_logs"
    source: |
      .service = "docker"

# 輸出設定:傳送至 Loki
sinks:
  loki:
    type: "loki"
    inputs:
      - "parse_logs"
    endpoint: "http://loki:3100"
    labels:
      app: "{{ container_name }}"
    encoding:
      codec: "json"

Vector 設定解密

  • sources 區塊定義了日誌來源為 Docker 容器,使用 docker_logs 類別收集所有容器的日誌
  • transforms 區塊設定了日誌轉換邏輯,這裡新增了一個 service 標籤用於識別來源
  • sinks 區塊設定了日誌的輸出目標,指定將日誌以 JSON 格式傳送到 Loki 服務

Loki 日誌儲存設定

Loki 作為日誌儲存和查詢系統,需要適當的設定來確保效能和可靠性。以下是基礎設定檔案 loki-config.yaml

auth_enabled: false
server:
  http_listen_port: 3100
  grpc_listen_port: 9096
  log_level: debug

storage:
  filesystem:
    chunks_directory: /tmp/loki/chunks
    rules_directory: /tmp/loki/rules
  replication_factor: 1

schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  metric_aggregation_enabled: true

Loki 設定解密

  • server 設定義了 HTTP 和 gRPC 的監聽埠
  • storage 設定了檔案系統儲存位置和複製因子
  • schema_config 設定了資料儲存架構,包含時間序列資料函式庫SDB)的設定
  • limits_config 啟用了指標聚合功能

Tempo 分散式追蹤設定

Tempo 負責處理分散式追蹤資料,需要較為複雜的設定來確保追蹤資料的收集和儲存。以下是 tempo-config.yaml 的設定:

server:
  http_listen_port: 3200
  grpc_listen_port: 9095

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: "0.0.0.0:4317"
        http:
          endpoint: "0.0.0.0:4318"

storage:
  trace:
    backend: local
    local:
      path: /var/tempo/blocks

metrics_generator:
  storage:
    path: /var/tempo/generator/wal
  remote_write:
    - url: http://victoriametrics:9090/api/v1/write
      send_exemplars: true

Tempo 設定解密

  • server 設定義了 Tempo 的 Web 介面和 gRPC 監聽埠
  • distributor 設定了 OTLP(OpenTelemetry Protocol)接收器的端點
  • storage 設定了追蹤資料的本地儲存路徑
  • metrics_generator 設定了指標產生和遠端寫入的相關設定

在實際佈署時,玄貓建議先在測試環境中驗證這些設定,確保各元件之間能夠正常通訊。監控系統的穩定性直接影響到我們對應用程式問題的排查效率,因此合適的設定和定期的維護都是不可或缺的。

這套監控設定能讓我們完整掌握容器化應用的執行狀況,從日誌收集、儲存到分散式追蹤,建立了一個完整的可觀測性平台。透過這些工具的協同運作,我們能更有效地監控和診斷系統問題,提升整體服務的可靠性。

我會將這份關於可觀測性基礎設施設定的文章重新改寫,保留重要技術細節同時加入我的經驗分享。

在建置大型系統監控平台的過程中,玄貓發現一個完整的可觀測性解決方案對於系統穩定性至關重要。這篇文章將分享如何設定並整合 OpenTelemetry Collector、Grafana 等工具,建立一套完整的監控系統。

OpenTelemetry Collector 設定詳解

首先讓我們來看 OpenTelemetry Collector 的核心設定檔案。以下是經過最佳化的 otel-collector-config.yml 設定:

extensions:
  health_check:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"
      http:
        endpoint: "0.0.0.0:4318"

processors:
  batch:
    timeout: 5s
    send_batch_size: 1024
  memory_limiter:
    check_interval: 5s
    limit_mib: 512
    spike_limit_mib: 128

exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"
  otlp:
    endpoint: "tempo:4317"
    tls:
      insecure: true
  otlp/jaeger:
    endpoint: "jaeger:4317"
    tls:
      insecure: true
  otlphttp/logs:
    endpoint: "http://loki:3100/otlp"
    tls:
      insecure: true
  debug:

service:
  extensions: [health_check]
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus, debug]
    traces:
      receivers: [otlp]
      processors: [batch, memory_limiter]
      exporters: [otlp, otlp/jaeger, debug]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/logs, debug]

內容解密

讓我逐項解釋這個設定檔的重要部分:

  1. 接收器(Receivers)設定

    • 設定了 gRPC(4317 埠)和 HTTP(4318 埠)兩種協定
    • 使用 “0.0.0.0” 表示接受來自任何網路介面的連線
  2. 處理器(Processors)設定

    • batch 處理器:設定 5 秒超時和 1024 的批次大小,這是我在高流量環境中測試出的最佳平衡點
    • memory_limiter:透過設定 512MB 限制和 128MB 尖峰限制來保護系統資源
  3. 匯出器(Exporters)設定

    • prometheus:用於指標收集
    • otlp:連線到 Tempo 進行分散式追蹤
    • jaeger:提供額外的追蹤視覺化選項
    • otlphttp/logs:將日誌傳送到 Loki
  4. 管道(Pipelines)設定

    • 分別為指標、追蹤和日誌建立獨立的處理管道
    • 每個管道都設定了適當的處理器和匯出器

VictoriaMetrics 採集設定

接下來是 VictoriaMetrics 的採集設定檔案 victoriametrics-scrape.yml

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'otel-collector'
    scrape_interval: 5s
    metrics_path: '/metrics'
    static_configs:
      - targets:
        - 'otel-collector:8889'

  - job_name: 'containeradvisor'
    scrape_interval: 5s
    static_configs:
      - targets: ['cadvisor:8080']

內容解密

這個設定檔主要設定了兩個監控目標:

  1. otel-collector

    • 每 5 秒從 OpenTelemetry Collector 採集一次指標
    • 透過 8889 埠存取指標端點
  2. containeradvisor

    • 同樣採用 5 秒的採集間隔
    • 從 cAdvisor 的 8080 埠收集容器指標

系統佈署與存取設定

完成設定後,使用以下指令啟動服務:

docker compose up -d

Grafana 設定步驟

  1. 使用預設的管理員帳號密碼(admin/admin)登入
  2. 首次登入時系統會要求更改密碼
  3. 新增 VictoriaMetrics 資料來源:
    • 進入 Connections -> Add new connection
    • 選擇 VictoriaMetrics
    • URL 設定為 http://victoriametrics:8428

在多年的監控系統建置經驗中,玄貓發現 VictoriaMetrics 相較於 Prometheus 在大規模環境下有更好的效能表現,特別是在處理長期資料儲存時。不過,對於小型專案,兩者的差異並不明顯。

安全性考量與存取控制

在實際佈署環境中,安全性是一個重要考量。玄貓建議採用以下方式之一來保護您的監控系統:

  1. Nginx 反向代理方案

    • 優點:設定簡單,整合 nginx-proxy 後維護方便
    • 缺點:安全性相對較低,需要額外的安全措施
  2. 虛擬私人網路 存取方案

    • 優點:提供更高的安全性保障
    • 缺點:需要額外的 虛擬私人網路 基礎設施

在生產環境中,玄貓更傾向使用 虛擬私人網路 方案,因為它能提供更好的存取控制和安全性。但對於開發環境或較小規模的佈署,使用 Nginx 反向代理也是可接受的選擇。

透過這套設定,我們建立了一個完整的監控系統,能夠收集並視覺化系統的指標、追蹤和日誌。這不僅有助於問題診斷,也能幫助我們更好地理解系統行為和效能特徵。在實際使用過程中,建議根據系統規模和需求適當調整設定引數,以達到最佳效果。

在現代微服務架構中,可觀測性(Observability)已經成為系統設計不可或缺的一環。作為一位資深的後端架構師,玄貓今天要分享如何在 Laravel 專案中建立完整的可觀測性架構,包含日誌(Logging)、追蹤(Tracing)和指標(Metrics)這三大支柱。

架構設計考量

在建置可觀測性架構時,我們需要考慮幾個關鍵因素:

基礎設施層面

  • 使用 Vector 自動收集基礎設施日誌
  • 採用 OpenTelemetry 收集應用程式層面的可觀測性資料
  • 透過 Grafana 實作統一的監控視覺化

應用程式層面

  • 確保日誌中包含足夠的連貫的背景與環境資訊
  • 實作分散式追蹤以掌握請求流程
  • 收集關鍵業務指標進行監控

SSH 隧道設定

為了方便存取監控介面,我們可以設定 SSH 隧道。在 ~/.bashrc~/.zshrc 中加入以下別名設定:

alias obs='ssh -L 3000:localhost:3000 user@your-vps'

設定完成後,只要執行 obs 指令就能建立通往往 Grafana 的安全通道。

Laravel 專案整合步驟

Docker 網路設定

首先在 docker-compose.yaml 中加入監控網路設定:

networks:
  monitoring:
    external: true

services:
  app:
    networks:
      - monitoring

安裝相依套件

在 Dockerfile 中加入必要的 PHP 擴充套件:

pecl install opentelemetry grpc

接著安裝 Laravel 所需的套件:

composer require laraotel/opentelemetry-laravel
composer require open-telemetry/transport-grpc

設定 OpenTelemetry

發布 OpenTelemetry 設定檔:

php artisan vendor:publish --provider="Keepsuit\LaravelOpenTelemetry\LaravelOpenTelemetryServiceProvider" --tag="opentelemetry-config"

config/logging.php 中設定 OTLP 日誌驅動:

'otlp' => [
    'driver' => 'monolog',
    'handler' => \Keepsuit\LaravelOpenTelemetry\Support\OpenTelemetryMonologHandler::class,
    'level' => 'debug',
]

環境變數設定

.env 檔案中加入必要的設定:

OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4317"
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
LOG_CHANNEL=otlp

測試路由實作

routes/web.php 中建立測試路由:

Route::get('/up', function () {
    Log::notice("開始從資料函式庫使用者資料");
    $user = User::all();
    Log::error("這是一條測試用的錯誤日誌");
    return response()->json($user);
});

DatabaseSeeder.php 中新增測試資料:

public function run()
{
    User::factory(1000)->create();
}

經過這些設定,我們建立了一個完整的可觀測性架構。系統不僅能收集基礎設施的日誌,還能追蹤應用程式層面的效能指標和異常狀況。透過整合 OpenTelemetry,我們可以在 Grafana 中統一檢視所有監控資訊,大幅提升系統的可維護性。

在實務經驗中,這套架構幫助我快速定位過多次系統異常,特別是在處理分散式系統的效能問題時,分散式追蹤的功能更是不可或缺。透過完整的可觀測性架構,開發團隊能夠更有效地進行問題排查與系統最佳化。 讓我重新組織這篇關於可觀測性與監控的技術文章,以更結構化和專業的方式呈現。

在現代微服務架構中,可觀測性(Observability)已成為確保系統穩定性和效能的關鍵要素。玄貓在多年的系統架構經驗中發現,建立完善的監控體系不僅能夠幫助我們及時發現問題,更能提供寶貴的系統效能最佳化方向。今天將分享如何在 Laravel 應用程式中建構全方位的可觀測性系統。

追蹤系統的實作與整合

OpenTelemetry 追蹤實作

在建構分散式系統追蹤時,玄貓建議使用 keepsuit/laravel-opentelemetry 套件,這個套件提供了豐富的追蹤功能:

composer require keepsuit/laravel-opentelemetry

這個套件會自動追蹤以下關鍵操作:

  • HTTP 伺服器請求(Laravel 路由)
  • HTTP 客戶端(外部服務呼叫)
  • 資料函式庫
  • Redis 操作
  • 佇列處理

整合 Grafana 與 Jaeger

當我們完成追蹤系統的佈署後,可以透過 Grafana 的 Explore 功能檢視追蹤資料。讓我分享一個實際案例:在一個電商平台的訂單處理系統中,我們透過追蹤發現某些資料庫存在效能瓶頸。

指標收集與監控

Prometheus 指標收集器的設定

在指標收集方面,玄貓推薦使用 spatie/laravel-prometheus 套件:

composer require spatie/laravel-prometheus
composer require laravel/horizon  // 可選,用於 Queue 監控

安裝完成後,執行以下命令:

php artisan prometheus:install
php artisan horizon:install

自訂請求指標中介軟體

為了更全面地監控系統效能,玄貓建議實作自訂的請求指標中介軟體:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Cache;
use Prometheus\CollectorRegistry;

class RequestMetricsMiddleware
{
    private $registry;
    
    public function __construct(CollectorRegistry $registry) 
    {
        $this->registry = $registry;
    }

    public function handle($request, Closure $next)
    {
        $start = microtime(true);
        $response = $next($request);
        $duration = microtime(true) - $start;
        
        // 記錄請求計數
        $counter = $this->registry->getOrRegisterCounter('app', 'http_requests_total', 'Total HTTP requests');
        $counter->inc();
        
        // 記錄請求延遲
        $histogram = $this->registry->getOrRegisterHistogram(
            'app',
            'http_request_duration_seconds',
            'HTTP request duration in seconds',
            ['route']
        );
        $histogram->observe($duration, ['route' => $request->route()->getName() ?? 'unnamed']);
        
        return $response;
    }
}

這個中介軟體不只記錄請求次數,還收集了請求處理時間的分佈情況,這對於效能最佳化非常有幫助。在我的實務經驗中,這些指標幫助我們找出了多個潛在的效能問題。

VictoriaMetrics 設定與整合

為了確保指標能被正確收集,需要在 VictoriaMetrics 的設定檔中加入適當的抓取設定:

- job_name: 'laravel_production'
  scrape_interval: 15s
  metrics_path: '/prometheus'
  static_configs:
    - targets: ['app:80']
  labels:
    environment: 'production'
    application: 'laravel_main'

在實際佈署經驗中,玄貓建議根據不同環境(開發、測試、正式)設定不同的 job_name,這樣能更清楚地區分和管理各環境的監控資料。

在建立完整的可觀測性系統後,我們能夠更有效地監控應用程式的健康狀況,快速診斷問題,並持續最佳化系統效能。這不僅提高了系統的可靠性,也大幅降低了維運成本。特別是在處理高流量服務時,完善的監控系統往往能幫助我們提前發現並解決潛在問題。

使用 Golang 建立可觀察性監控系統

在這個部分,玄貓將帶領大家建立一個完整的 Golang 應用程式監控系統。我們將從簡單的基礎開始,逐步建立一個具有結構化日誌、分散式追蹤和指標收集功能的應用程式。

首先,讓我們建立一個基礎的 HTTP 服務:

package main

import (
    "fmt"
    "log"
    "net/http"
)

// helloHandler 處理 /hello 路由的請求
func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, Observability!")
}

func main() {
    // 註冊路由處理器
    http.HandleFunc("/hello", helloHandler)
    
    // 啟動 HTTP 伺服器
    log.Println("Starting server on :8080...")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("Server failed to start:", err)
    }
}

** **

  1. 這個基礎程式碼建立了一個簡單的 HTTP 伺服器,監聽 8080 連線埠
  2. helloHandler 函式處理 /hello 路徑的 GET 請求,回傳一個簡單的問候訊息
  3. 使用 http.HandleFunc 註冊路由處理器
  4. 使用 http.ListenAndServe 啟動伺服器
  5. 程式包含基本的錯誤處理和日誌記錄

接下來,讓我們新增 Docker 設定,使其能在容器環境中執行:

FROM golang:1.21-alpine

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o main .

EXPOSE 8080

CMD ["./main"]

** **

  1. 使用 golang:1.21-alpine 作為基礎映像,提供輕量級的執行環境
  2. 設定工作目錄為 /app
  3. 先複製並下載依賴,利用 Docker 的層級快取機制
  4. 複製所有原始碼並編譯
  5. 開放 8080 連線埠
  6. 設定容器啟動命令

為了整合這個服務到我們的可觀察性架構中,我們需要新增以下功能:

  1. 結構化日誌:使用 zerolog 取代標準日誌
  2. 分散式追蹤:整合 OpenTelemetry
  3. 應用程式指標:加入 Prometheus 指標收集
  4. 健康檢查端點:監控應用程式狀態

在下一個部分,玄貓將詳細介紹如何實作這些功能,並展示如何將它們整合到我們現有的監控堆積積疊中。這些工具將幫助我們更好地理解應用程式的執行狀況,快速診斷問題,並確保系統的可靠性。

在現代微服務架構中,可觀測性(Observability)已經成為不可或缺的重要元素。玄貓今天要分享如何建立一個具備完整可觀測性的 Go 微服務,從基礎的 HTTP 服務擴充套件到結構化日誌系統。

基礎 HTTP 服務實作

首先,讓我們從一個簡單的 HTTP 服務開始:

package main

import (
    "log"
    "net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("Received request to /hello endpoint")
    w.Write([]byte("Hello, Observability!"))
}

func main() {
    http.HandleFunc("/hello", helloHandler)
    
    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatalf("Server failed: %s", err)
    }
}

** **

  1. 這段程式碼建立了一個基本的 HTTP 伺服器,監聽在 8080 埠口
  2. /hello 路徑的處理函式會回傳一個簡單的問候訊息
  3. 使用標準的 log 套件進行基礎日誌記錄
  4. 可以透過瀏覽器或 curl 存取 http://localhost:8080/hello 測試服務

進階結構化日誌整合

隨著服務規模成長,我們需要更強大的日誌系統。Go 1.21 引入的 slog 套件提供了結構化日誌功能,讓我們結合 OpenTelemetry 進行升級:

package main

import (
    "context"
    "fmt"
    
    "go.opentelemetry.io/contrib/bridges/otelslog"
    "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
    "go.opentelemetry.io/otel/log/global"
    "go.opentelemetry.io/otel/sdk/log"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/semconv"
)

func initLogger(ctx context.Context) func() {
    logExporter, err := otlploggrpc.New(ctx,
        otlploggrpc.WithEndpoint("otel-collector:4317"),
        otlploggrpc.WithInsecure(),
    )
    if err != nil {
        panic(fmt.Sprintf("failed to initialize OTLP log exporter: %v", err))
    }

    res, err := resource.New(ctx,
        resource.WithAttributes(semconv.ServiceNameKey.String("go-app")),
    )
    if err != nil {
        panic(fmt.Sprintf("resource creation error: %v", err))
    }

    lp := log.NewLoggerProvider(
        log.WithResource(res),
        log.WithProcessor(log.NewBatchProcessor(logExporter)),
    )
    
    global.SetLoggerProvider(lp)
    slog.SetDefault(otelslog.NewLogger("go-app"))

    return func() {
        if err := lp.Shutdown(ctx); err != nil {
            panic(fmt.Sprintf("failed to shutdown logger provider: %v", err))
        }
    }
}

** **

  1. 引入 OpenTelemetry 相關套件,包含結構化日誌、OTLP 匯出器等
  2. 建立 OTLP 日誌匯出器,設定連線到 OpenTelemetry Collector
  3. 設定資源屬性,標記服務名稱為 “go-app”
  4. 建立日誌提供者(LoggerProvider),使用批次處理器提升效能
  5. 全域設定 otelslog 日誌器,自動加入服務名稱和追蹤資訊
  6. 回傳清理函式,確保應用程式優雅關閉時能正確處理日誌系統

這樣的結構化日誌系統帶來幾個重要優勢:

  1. 結構化資料:日誌不再是純文字,而是結構化的 JSON 格式,便於分析和查詢
  2. 追蹤整合:自動包含 trace_id 和 span_id,方便關聯請求與日誌
  3. 標準化輸出:透過 OpenTelemetry 協定,確保日誌格式符合業界標準
  4. 效能最佳化:使用批次處理器,減少 I/O 操作提升效能

玄貓在實際專案中發現,這種結構化的日誌系統特別適合微服務架構,因為它能夠:

  • 快速定位問題:透過 trace_id 追蹤請求在不同服務間的流向
  • 最佳化效能:批次處理減少了系統資源消耗
  • 標準化監控:與業界標準工具整合,降低維運成本

在建置分散式系統時,良好的可觀測性設計能夠顯著減少問題排查時間,提升系統可靠性。結構化日誌是實作這一目標的重要根本。

進階的日誌系統讓我們能夠更有效地監控和維護微服務,不僅提供了更豐富的資訊,還能與現代化的監控工具無縫整合。這套系統在玄貓負責的多個大型專案中都發揮了關鍵作用,協助團隊快速發現和解決問題。 在這個部分,我們要為我們的應用程式加入結構化日誌(Structured Logging)和分散式追蹤(Distributed Tracing)功能。讓玄貓帶領大家一步實作這些重要的可觀察性(Observability)功能。

實作結構化日誌處理器

首先,讓我們擴充套件 helloHandler 函式,加入結構化日誌功能:

package main

func helloHandler(w http.ResponseWriter, r *http.Request) {
    // 取得請求的連貫的背景與環境
    ctx := r.Context()

    // 記錄請求資訊
    slog.InfoContext(ctx, "處理 /hello 請求",
        "method", r.Method,
        "user_agent", r.UserAgent())

    // 回應請求
    _, err := fmt.Fprintln(w, "Hello, Observability!")
    if err != nil {
        slog.Error("回應寫入失敗", "error", err)
        return
    }
}

程式碼解密:

  1. ctx := r.Context() - 取得 HTTP 請求的連貫的背景與環境,這將用於關聯日誌和追蹤資訊
  2. slog.InfoContext() - 使用結構化方式記錄請求資訊,包含:
    • 請求方法(GET、POST等)
    • 使用者代理(User-Agent)資訊
  3. 使用 fmt.Fprintln() 傳送回應,並進行錯誤處理
  4. 如果發生錯誤,使用 slog.Error() 記錄錯誤資訊

設定分散式追蹤

接下來,玄貓將示範如何設定 OpenTelemetry 追蹤功能。首先,需要安裝必要的套件:

go get go.opentelemetry.io/otel/sdk/trace \
    go.opentelemetry.io/otel/trace \
    go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc \
    go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp \
    go.opentelemetry.io/otel/sdk/resource \
    go.opentelemetry.io/otel/semconv/v1.30.0

然後,實作追蹤器的初始化函式:

package main

import (
    "context"
    "fmt"
    "os"
    "time"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
)

func initTracer(ctx context.Context) func() {
    // 建立 OTLP 追蹤匯出器
    exp, err := otlptracegrpc.New(ctx,
        otlptracegrpc.WithEndpoint("otel-collector:4317"),
        otlptracegrpc.WithInsecure(),
    )
    if err != nil {
        panic("追蹤匯出器建立失敗: " + err.Error())
    }

    // 建立資源描述
    res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceNameKey.String("go-app"),
        ),
    )
    if err != nil {
        panic(fmt.Sprintf("資源建立錯誤: %v", err))
    }

    // 設定追蹤提供者
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exp,
            sdktrace.WithMaxExportBatchSize(512),
            sdktrace.WithBatchTimeout(5*time.Second),
        ),
        sdktrace.WithResource(res),
    )

    // 設定全域追蹤提供者
    otel.SetTracerProvider(tp)

    // 回傳清理函式
    return func() {
        _ = tp.Shutdown(context.Background())
    }
}

程式碼解密:

  1. otlptracegrpc.New() - 建立 OTLP gRPC 追蹤匯出器:
    • 設定 Collector 端點為 otel-collector:4317
    • 使用不安全連線(開發環境使用)
  2. resource.New() - 建立資源描述:
    • 設定服務名稱為 go-app
  3. sdktrace.NewTracerProvider() - 建立追蹤提供者:
    • 設定批次大小上限為 512
    • 設定批次超時間為 5 秒
  4. otel.SetTracerProvider() - 設定全域追蹤提供者
  5. 回傳清理函式,用於程式結束時優雅關閉追蹤器

這樣的設定可以確保我們的應用程式產生的追蹤資料能夠被正確收集和傳送到 OpenTelemetry Collector。接下來,玄貓將說明如何整合這些功能到我們的 HTTP 處理器中。

在建置現代微服務架構時,分散式追蹤(Distributed Tracing)已成為不可或缺的觀測工具。玄貓在多年的系統架構經驗中,發現有效的追蹤機制不僅能幫助開發團隊快速定位問題,更能提供系統效能最佳化的重要依據。讓我們探討如何在 Go 應用程式中實作 OpenTelemetry 追蹤系統。

OTLP 匯出器與 TracerProvider 設定

在實作分散式追蹤時,首先需要建立正確的 OTLP 匯出器設定。以下是一個完整的示範:

func initTracer(ctx context.Context) func() error {
    res := resource.NewWithAttributes(
        semconv.SchemaURL,
        semconv.ServiceNameKey.String("go-microservice"),
    )
    
    tp := trace.NewTracerProvider(
        trace.WithResource(res),
        trace.WithBatcher(otlpExporter),
    )
    
    otel.SetTracerProvider(tp)
    return tp.Shutdown
}

這段程式碼主要完成幾個重要工作:

  • 建立資源(Resource)定義,標記服務名稱
  • 設定 TracerProvider 並整合 OTLP 匯出器
  • 設定全域 TracerProvider 例項
  • 提供優雅關閉機制

HTTP 伺服器的追蹤整合

在建置支援分散式追蹤的 HTTP 伺服器時,我們可以利用 otelhttp 中介軟體自動為每個請求建立追蹤範圍:

mux := http.NewServeMux()
mux.Handle("/hello", otelhttp.NewHandler(
    http.HandlerFunc(helloHandler),
    "/hello"
))

這個設計讓每個進入 /hello 端點的請求都會自動建立一個追蹤範圍(Span)。玄貓在實務經驗中發現,這種自動化的追蹤機制大幅減少了手動埋點的工作量。

處理函式中的追蹤實作

在實際的請求處理中,我們可以加入更細緻的追蹤邏輯:

func helloHandler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    
    tracer := otel.Tracer("go-app")
    ctx, span := tracer.Start(ctx, "businessLogic")
    defer span.End()
    
    // 執行業務邏輯
    time.Sleep(100 * time.Millisecond)  // 模擬處理時間
    
    slog.InfoContext(ctx, "完成請求處理",
        "method", r.Method,
        "path", r.URL.Path,
    )
    
    fmt.Fprintln(w, "Hello, Observability!")
}

這個處理函式展示了幾個關鍵概念:

  • 建立子追蹤範圍來追蹤特定業務邏輯
  • 使用結構化日誌記錄關鍵事件
  • 確保追蹤範圍的正確結束

應用程式啟動設定

最後,在主程式中整合所有元件:

func main() {
    ctx := context.Background()
    
    shutdownTracer := initTracer(ctx)
    defer shutdownTracer()
    
    shutdownLogger := initLogger(ctx)
    defer shutdownLogger()
    
    mux := http.NewServeMux()
    mux.Handle("/hello", otelhttp.NewHandler(
        http.HandlerFunc(helloHandler),
        "/hello"
    ))
    
    srv := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }
    
    slog.Info("啟動伺服器", "port", 8080)
    if err := srv.ListenAndServe(); err != nil {
        slog.Error("伺服器啟動失敗", "error", err)
        os.Exit(1)
    }
}

透過這樣的設定,我們建立了一個完整的可觀測性系統,能夠收集並分析分散式追蹤資料。這些追蹤資訊將透過 OpenTelemetry Collector 傳送到 Jaeger、Tempo 等視覺化工具,幫助開發團隊更好地理解系統行為。

在實務應用中,玄貓建議根據專案需求適當調整追蹤的粒度。過多的追蹤點可能造成效能負擔,而過少則可能無法提供足夠的診斷資訊。找到適當的平衡點是建置高效能可觀測系統的關鍵。

在微服務架構日益複雜的今日,完善的分散式追蹤機制已成為系統可靠性的根本。透過 OpenTelemetry 的整合,我們不僅能夠快速定位問題,更能持續最佳化系統效能,為服務品質提供強而有力的保障。

Prometheus 與 VictoriaMetrics 的指標收集設定

在這個技術實作中,我們將使用 Prometheus 的標準函式庫lient_golang 來收集指標。這個函式庫我們以 VictoriaMetrics 可收集的格式發布指標。不同於追蹤和日誌,我們不會透過 OTEL Collector 傳送指標,而是建立一個 /metrics 端點並設定指標收集。

安裝必要的依賴套件

首先,讓我們加入 Prometheus 客戶端依賴:

go get github.com/prometheus/client_golang/prometheus \
github.com/prometheus/client_golang/prometheus/promhttp

定義核心指標

我們將定義兩個基本的指標:HTTP 請求計數器和回應時間的直方圖。這些指標會在程式初始化時註冊:

package main

import "github.com/prometheus/client_golang/prometheus"

var (
    requestCount = prometheus.NewCounter(prometheus.CounterOpts{
        Name: "goapp_http_requests_total",
        Help: "Total number of HTTP requests handled by the Go app",
    })
    
    responseDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
        Name:    "goapp_response_duration_seconds",
        Help:    "Histogram of response durations for /hello",
        Buckets: prometheus.DefBuckets,
    })
)

func init() {
    prometheus.MustRegister(requestCount, responseDuration)
}

在處理函式中實作指標收集

接著,在我們的 HTTP 處理函式中整合這些指標:

package main

func helloHandler(w http.ResponseWriter, r *http.Request) {
    timer := prometheus.NewTimer(responseDuration)
    defer timer.ObserveDuration()
    
    requestCount.Inc()
    
    _, err := fmt.Fprintln(w, "Hello, Observability!")
    if err != nil {
        slog.Error("Failed to write response", "error", err)
        return
    }
}

建立指標端點

最後,我們需要建立一個 /metrics 端點來發布這些指標:

package main

import "github.com/prometheus/client_golang/prometheus/promhttp"

func main() {
    // ... 其他設定 ...
    mux.Handle("/metrics", promhttp.Handler())
    // ... 啟動伺服器 ...
}

指標解密

讓我們來解析這些程式碼的重要概念:

  1. 計數器(Counter)設計

    • goapp_http_requests_total 是一個只會增加不會減少的計數器
    • 用來追蹤總請求數,這是監控系統負載的基本指標
  2. 時間直方圖(Histogram)實作

    • goapp_response_duration_seconds 記錄請求處理時間
    • 使用預設的時間區間(buckets)來分類別不同的回應時間
    • 可用於分析效能分佈和異常情況
  3. 計時器使用

    • prometheus.NewTimer() 自動處理時間計算
    • defer timer.ObserveDuration() 確保在函式結束時記錄持續時間
    • 這種方式既簡潔又能避免手動計時的錯誤
  4. 指標序號產生器制

    • init() 函式中註冊確保程式啟動時就準備好收集指標
    • MustRegister 在註冊失敗時會導致程式當機,這是合理的,因為指標收集是關鍵功能

指標收集系統整合

經過以上設定後,我們的應用程式就能在 http://localhost:8080/metrics 端點提供指標資料。VictoriaMetrics 可以定期存取這個端點來收集指標。這些指標資料可以用來:

  • 監控應用程式的健康狀態
  • 分析效能瓶頸
  • 設定告警閾值
  • 生成營運報表

這種指標收集架構提供了即時與可靠的效能監控,讓玄貓在處理高流量服務時能夠及時發現並解決問題。透過這些指標,我們不只能瞭解系統的當前狀態,還能預測潛在的問題,這對於維護高用性服務至關重要。

在現代微服務架構中,建立完善的可觀測性(Observability)架構至關重要。玄貓今天將分享如何為 Go 微服務整合監控系統,實作全方位的系統監控。

VictoriaMetrics 監控設定

首先,我們需要在 VictoriaMetrics 中新增 Go 服務的監控任務。在設定檔案中加入以下內容:

- job_name: 'go_app'
  scrape_interval: 15s
  static_configs:
    - targets: [ 'go-app:8080' ]

這個設定指定了:

  • 每 15 秒從 Go 應用程式收集一次指標資料
  • 目標服務名稱為 go-app,監聽連線埠為 8080
  • VictoriaMetrics 將自動存取 /metrics 端點的監控資料

設定完成後,執行以下指令重新啟動 VictoriaMetrics 服務:

docker compose restart victoriametrics

Docker 容器設定與佈署

Dockerfile 設定

讓我們建立一個最佳化的多階段建構 Dockerfile:

FROM golang:1.24-alpine AS builder
WORKDIR /build
COPY ./go.mod ./go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o go-app main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates && \
    addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /build/go-app /app/go-app
USER appuser
WORKDIR /app
EXPOSE 8080
ENTRYPOINT ["/app/go-app", "sync", "-d"]

這個 Dockerfile 包含了多項最佳實踐:

  • 使用多階段建構減少最終映像大小
  • 採用 Alpine 基礎映像進一步最佳化容器體積
  • 實施最小許可權原則,使用非 root 使用者執行應用
  • 最佳化建構引數,減少執行檔大小

Docker Compose 服務設定

在 docker-compose.yml 中加入 Go 服務的設定:

services:
  go-app:
    build: .
    container_name: go-app
    ports:
      - "8080:8080"
    networks:
      - monitoring

networks:
  monitoring:
    external: true

這個設定確保了:

  • 服務可以透過 8080 連線埠對外提供服務
  • 容器連線到監控網路,實作與其他監控元件的通訊

佈署服務只需執行:

docker compose up -d --build go-app

監控系統驗證

指標監控

存取 http://localhost:8080/hello 產生一些測試流量後,在 Grafana 的 Metrics 面板中查詢 goapp_http_requests_total 指標,就能看到請求量的即時統計圖表。

分散式追蹤

在 Grafana 的 Explore 頁面選擇 Jaeger 或 Tempo 資料源,搜尋 go-app 服務,即可檢視詳細的請求追蹤資訊,包含請求路徑、延遲時間等關鍵指標。

日誌管理

透過 Grafana 的 Logs 面板,我們可以集中檢視 Go 服務的所有日誌資訊,方便進行問題診斷和系統監控。

建立這套完整的可觀測性架構後,玄貓發現開發團隊能更快速地發現和解決問題,大幅提升了系統的可維護性。透過指標、追蹤和日誌的三位一體監控,我們不僅能夠即時發現異常,還能深入分析系統行為,確保服務的穩定執行。這套架構特別適合在微服務架構中實施,能有效支援系統的持續成長和最佳化。 由於提供的內容片段似乎是一個投票或評論系統的介面元素,與包含不完整的登入連結和混合的俄語文字,這些內容並不適合作為技術文章的一部分。對此類別內容,我們將跳過處理,因為:

  1. 這不是有意義的技術內容
  2. 內容片段不完整與混合多種語言
  3. 包含網站特定的功能元素(如登入連結和投票系統)