iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Software Development

學校沒教的後端生存指南:30 天打造非同步 S3 微服務,部署 K8s 實現 HA 架構系列 第 17

[ Day 17 ] 容器化魔法:Docker Compose 一鍵建立 App + Prometheus + Grafana 監控服務

  • 分享至 

  • xImage
  •  

昨天我們成功運用多階段建置(Multi-stage Build)技術,將非同步 S3 微服務打包成僅有 500MB 的極輕量 Docker 容器,並在獨立隔離的環境中順利跑起來。

不過如果要做壓力測試的話,又要怎麼知道目前系統的問題點在哪裡呢?目前我們的微服務雖然做了很多錯誤處理方式,例如Double-check、Semaphore、Resilience4j降級等等,但是當一直出現錯誤時我們要怎麼快速找到系統的問題點並且對症下藥呢?要怎麼確定是程式碼導致系統效能不足?又或是程式碼沒問題瓶頸其實是在硬體環境上呢?

這時即時監控就非常重要,如果沒有監控,進行壓力測試就跟盲人摸象一樣,不知道系統什麼時候 CPU 達到 100% 飽和、不知道記憶體是接近崩潰還是游刃有餘,更不知道 Netty EventLoop 是否發生了請求塞車。

今天,將帶大家了解監控黃金三角組合:Spring Boot App + Prometheus (時序資料庫) + Grafana (視覺化儀表板)!並且,教大家如何省去多次 docker run 指令,透過Docker Compose 進行容器編排,實現一行指令,一鍵建立全套視覺化監控服務!

觀念與架構:Prometheus + Grafana 監控黃金三角陣型

使用角色

  1. DevOps / SRE (系統可靠性工程師):負責 7x24 小時監控全公司的微服務健康度,設定告警門檻(例如:當 CPU 飆破 90% 或 5xx 錯誤率大於 1% 時自動觸發 Telegram / Slack 警報)。
  2. 後端架構師與開發團隊:在進行壓力測試、線上故障排查(Incident Post-mortem)、以及容量規劃(Capacity Planning)時使用。

使用情境

  1. 壓測觀察:即時查看併發衝擊下,系統吞吐量 (QPS) 與 P99 Latency 延遲變化。
  2. 線上日常監控:觀測 JVM Heap GC 回收頻率、Netty 記憶體使用率、與外部 AWS S3 呼叫的成功率。
  3. 緊急除錯:當線上 API 突發變慢時,透過圖表快速定位是資料庫卡住、記憶體洩漏(Memory Leak),還是外部 S3 機房延遲。

https://ithelp.ithome.com.tw/upload/images/20260913/20183864wp197VUQxl.png

分工職責

  1. Spring Boot Actuator & Micrometer(指標提供者): 在微服務內部默默收集 JVM 記憶體、G1GC 垃圾回收頻率、CPU 佔用率、HTTP QPS、Netty 記憶體等指標,並透過 /actuator/prometheus HTTP 端點暴露成標準格式的文字數據。

  2. Prometheus(指標收集器與時序資料庫 TSDB): 一個極輕量、專為監控設計的 TSDB 資料庫。它會以 Pull(拉取)模式,定時(例如每 5 秒)向我們的微服務發起 HTTP 請求,將最新的指標數值抓回並儲存起來。

  3. Grafana(視覺化儀表板大腦): 最頂尖的視覺化圖表引擎。它向 Prometheus 查詢歷史與即時數據,將數字繪製成絕美的即時動態曲線圖(CPU 飽和線、JVM Heap 鋸齒波形、HTTP 200/429/500 比例)。

Docker Compose

接觸 Docker 的工程師一定有遇過三個不同的服務分別包成容器,它們之間會不會遇到網路連線問題,還要手動去建立docker network,不然 Prometheus 就算知道 App 的 IP 也沒有辦法連線成功

透過 Docker Compose,我們可以省去執行三次 docker run和手動建立 Docker Network、傳遞複雜的 --link 或浮動 IP 參數的麻煩。因為一旦容器重啟 IP 改變,整個監控鏈路就會當場斷裂。換言之,Docker compose的優點如下

  1. 宣告式編排(Environment as Code):將所有相關聯的容器(App、Prometheus、Grafana)定義在一份 docker-compose.yml 檔案中。
  2. 內建自動 DNS 服務發現(Bridge Network):Docker Compose 會自動為這三個容器建立一個專屬的獨立 Bridge 網路(如 monitoring-net)。在同一個網路下,容器之間可以直接使用服務名稱(Service Name)互相存取!例如:Prometheus 只需要設定目標為 http://s3-app:8080,Docker 底層的 DNS 就會自動解析到 s3-app 容器的內部 IP,完全不需要擔心 IP 改變或網路卡住的問題!
  3. 一鍵掌控docker compose up -d 一鍵開機、docker compose down 一鍵關機。

實戰演練:配置元件與撰寫 docker-compose.yml

現在,我們打開之前的專案,按部就班地來建立配置文件

1. 微服務配置 (pom.xml與application.yml)

在前期的開發示範階段,直接將許多變數在 Java 程式碼中寫死例如aws ak, sk, bucketName等等,或是將參數散落在各個 @Value 中。但要寫好一個好的程式碼,必須落實 Cloud-Native 的 12-Factor App 原則:配置與程式碼嚴格解耦(Decoupling Config from Code)!

所以我們在resources/資料夾下新增一個application.yml 檔案,將變數移進去,好處有二:

  1. 集中化管理:環境變數、連線池上限、Actuator 監控開關一目瞭然,不需要開 Java 檔搜尋。
  2. 動態環境變數覆蓋application.yml 支援 ${VARIABLE:defaultValue} 語法。當微服務打包成 Docker 容器或部署到 K8s 時,我們可以直接從外部 Docker 容器環境變數注入真實的 AWS Credentials 與配置,完全不需要重新編譯 JAR 包

所以我們需要在application.yml 檔案中加入下列設定,包含s3變數以及後續要用到的prometheus 監控相關設定

server:
  port: ${SERVER_PORT:8080}

spring: 
  application: 
    name: s3-async-service

s3:
  bucket: ${S3_BUCKET:iron-netty}
  region: ${AWS_REGION:ap-southeast-2}
  semaphore-permits: ${SEMAPHORE_PERMITS:50}
  upload:
    max-concurrency: ${S3_UPLOAD_MAX_CONCURRENCY:50}

management: 
  endpoints: 
    web: 
      exposure: 
        include: "health,info,prometheus" # 開放 prometheus 端點 
  metrics: 
    tags: 
      application: ${spring.application.name}

resilience4j:
  // 之前建立過的,以下省略

pom.xml 中引入 Spring Boot Actuator 與 Micrometer Prometheus 依賴:

<!-- Spring Boot Actuator 監控模組 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- 轉換成 Prometheus 格式指標的 Micrometer 套件 -->
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

2. Prometheus 配置文件 (prometheus.yml)

在專案根目錄下新建 prometheus.yml 檔案,設定抓取目標:

global:
  scrape_interval: 5s # 每 5 秒抓取一次指標數據
  evaluation_interval: 5s

scrape_configs:
  - job_name: 's3-microservice-job'
    metrics_path: '/actuator/prometheus' # 指定 Actuator 端點
    static_configs:
      - targets: ['s3-app:8080'] # 利用 Docker Compose 內部 DNS!直接填容器服務名稱「s3-app」


3. 撰寫 docker-compose.yml

在專案根目錄下新建 docker-compose.yml

version: '3.8'

networks:
  monitoring-net:
    driver: bridge

services:
  # 1. 微服務應用容器
  s3-app:
    build:
      context: .
      dockerfile: Dockerfile
    image: my-app:v1
    container_name: s3-app
    ports:
      - "8080:8080"
    environment:
      - AWS_REGION=ap-northeast-1
      - AWS_ACCESS_KEY_ID={your_ak}
      - AWS_SECRET_ACCESS_KEY={your_sk}
    networks:
      - monitoring-net

  # 2. Prometheus 時序資料庫容器
  prometheus:
    image: prom/prometheus:v2.50.0
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    networks:
      - monitoring-net
    depends_on:
      - s3-app

  # 3. Grafana 視覺化儀表板容器
  grafana:
    image: grafana/grafana:10.3.3
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin # 設定預設管理員密碼為 admin
    networks:
      - monitoring-net
    depends_on:
      - prometheus

所以目前專案程式碼架構會是這樣:
https://ithelp.ithome.com.tw/upload/images/20260913/20183864DrZ3jHX5cR.png


啟動docker compose

當配置宣告完成後,真正的魔術時刻到了!打開 Terminal 終端機,執行:

# 因為我們有改動過程式碼,所以要加入--build重新打包一次image
docker compose up --build

如果執行成功沒有報錯會顯示以下畫面
https://ithelp.ithome.com.tw/upload/images/20260913/20183864lNpTkDPcxg.png

若使用背景執行docker compose up -d 可以透過 docker compose ps 查看狀態,可以看到三個容器全部在 monitoring-net 網路下以 Up 狀態完美連線並存!

https://ithelp.ithome.com.tw/upload/images/20260913/20183864hCHMrmbS64.png

接著雖然容器成功建立起來,但裡面的不同服務的連線還不一定有成功,我們先打開http://localhost:9090,點擊上方Narbar的 Status -> Targets 看看連線狀態,有時候可能會發生這樣的狀況,發現沒有連上我們的微服務,通常可能的原因是設定檔沒有調好,或是pom.xml更新後沒有重新建立image導致系統讀到舊的版本

https://ithelp.ithome.com.tw/upload/images/20260913/201838643K58sV6vdr.png

調整之後,正確畫面應該看到這樣Up
https://ithelp.ithome.com.tw/upload/images/20260913/201838648IhJNqmGSZ.png

Grafana 視覺化儀表板

  1. 登入 Grafana: 打開瀏覽器進入 http://localhost:3000,輸入預設帳號 admin / 密碼 admin 登入。然後他會要求你重設新密碼,看要設定或跳過這個階段都可以

https://ithelp.ithome.com.tw/upload/images/20260913/20183864H3Yy4jRlV8.png

  1. 新增 Prometheus 資料源(Data Source)
    • 點選 Data sources(Add data source) ➔ 選擇 Prometheus
    • 在Connection欄位中 Prometheus server URL 填入:http://prometheus:9090 (雖然我們在本地瀏覽起端是用http://localhost:9090來看頁面但因為grafana和prometheus都是由同一個docker compose建立的,所以我們需要使用 Docker 內部網域 DNS 來宣告連線)。
    • 點擊最下方的 Save & test,連線成功會出現綠色「Successfully queried the Prometheus API」
      https://ithelp.ithome.com.tw/upload/images/20260913/20183864fwCE4uKRUf.png

  1. 匯入 JVM 經典儀表板 (Import Dashboard)

    • 畫面中點擊 Dashboards(create your first dashboard)

    • 選擇 Import a dashboard

    • Import via grafana.com 欄位填入 ID 4701 下方json不用填,點擊 Load
      https://ithelp.ithome.com.tw/upload/images/20260913/20183864gJbvybLHcs.png

    • Data Source 選擇我們剛剛設定的 Prometheus,點擊 Import!
      https://ithelp.ithome.com.tw/upload/images/20260913/20183864bW9EDGODbG.png

  2. 設定好後回到dashboard介面看到這個就大功告成啦
    https://ithelp.ithome.com.tw/upload/images/20260913/201838648qPEO1btYm.png


總結

我們不僅重置了程式碼中全域變數的使用呼叫方式,也完成了容器化微服務最後一道關鍵的可觀測性(Observability)拼圖:

  1. 擺脫盲人摸象:釐清 Prometheus + Grafana 維運情境,為 Docker 容器裝上全天即時監控
  2. 掌握 Docker Compose 編排與 DNS:宣告 monitoring-net 橋接網路,解決容器通訊疑慮,實現微服務與監控網路設定
  3. 完成Prometheus + Grafana 設定:有了設定模板之後,後續壓力測試我們就可以快速到到問題點,馬上進行調整!

明天,我們就可以在使用JMeter進行壓力測試!並且使用這套完整的 Grafana 監控服務,看壓測的結果究竟是 CPU 衝上 95% 飽和線、Semaphore(50) 限流閥回傳 429 擋下海嘯,又或是JVM OOM (Out Of Memory) 崩潰!


上一篇
[ Day 16 ] 容器化服務 : 撰寫 Dockerfile 與多階段建置 (Multi-stage build) 環境隔離
系列文
學校沒教的後端生存指南:30 天打造非同步 S3 微服務,部署 K8s 實現 HA 架構17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言