Prometheus와 Grafana로 Golang 어플리케이션 모니터링하기

Prometheus와 Grafana로 Golang 어플리케이션 모니터링하기

Golang 어플리케이션을 컨테이너화하고 Prometheus와 Grafana로 모니터링하는 방법을 배워볼게요.

출처: 문서

본문

이 가이드는 Golang 어플리케이션을 컨테이너화하고 Prometheus와 Grafana로 모니터링하는 방법을 알려줘요.

감사의 말: Docker는 이 가이드에 기여한 Pradumna Saraf 님께 감사를 전해요.

개요

어플리케이션이 의도한 대로 동작하는지 확인하려면 모니터링이 중요해요. 가장 인기 있는 모니터링 도구 중 하나는 Prometheus예요. Prometheus는 신뢰성과 확장성을 위해 설계된 오픈소스 모니터링·경고 툴킷이에요. 모니터링 대상의 메트릭 HTTP 엔드포인트를 스크래핑해 대상으로부터 메트릭을 수집해요. 메트릭을 시각화하려면 Grafana를 사용할 수 있어요. Grafana는 메트릭이 어디에 저장되어 있든 쿼리·시각화·경고·이해할 수 있게 해주는 오픈소스 모니터링·관측성 플랫폼이에요.

이 가이드에서는 실제 어플리케이션을 시뮬레이션하기 위한 몇 가지 엔드포인트가 있는 Golang 서버를 만들 거예요. 그런 다음 Prometheus로 서버에서 메트릭을 노출하고, 마지막으로 Grafana로 메트릭을 시각화할 거예요. Golang 어플리케이션을 컨테이너화하고 Docker Compose 파일로 Golang, Prometheus, Grafana의 모든 서비스를 연결할 거예요.

무엇을 배울까요?

  • 커스텀 Prometheus 메트릭이 있는 Golang 어플리케이션 만들기
  • Golang 어플리케이션 컨테이너화하기
  • Docker Compose로 여러 서비스를 실행하고 연결해 Prometheus와 Grafana로 Golang 어플리케이션 모니터링하기
  • Grafana 대시보드로 메트릭 시각화하기

사전 요구사항

  • Golang에 대한 좋은 이해가 있다고 가정해요.
  • Prometheus와 Grafana에서 대시보드 만들기에 익숙해야 해요.
  • 컨테이너, 이미지, Dockerfile 같은 Docker 개념에 익숙해야 해요. Docker를 처음 접한다면 Docker 기초 가이드 로 시작할 수 있어요.

어플리케이션 빌드

사전 요구사항

git 클라이언트 가 있어야 해요. 이 섹션의 예제는 명령줄 기반 git 클라이언트를 사용하지만, 어떤 클라이언트든 사용할 수 있어요.

이 가이드와 함께 사용할 샘플 어플리케이션을 클론해주세요.

$ git clone https://github.com/dockersamples/go-prometheus-monitoring.git

클론하면 go-prometheus-monitoring 디렉터리 안에 다음 콘텐츠 구조가 보일 거예요:

go-prometheus-monitoring
├── CONTRIBUTING.md
├── Docker
│   ├── grafana.yml
│   └── prometheus.yml
├── dashboard.json
├── Dockerfile
├── LICENSE
├── README.md
├── compose.yaml
├── go.mod
├── go.sum
└── main.go
  • main.go : 어플리케이션의 엔트리 포인트.
  • go.mod 와 go.sum : Go 모듈 파일.
  • Dockerfile : 앱을 빌드하는 데 사용하는 Dockerfile.
  • Docker/ : Grafana와 Prometheus용 Docker Compose 구성 파일.
  • compose.yaml : 모든 것(Golang 앱, Prometheus, Grafana)을 실행하는 Compose 파일.
  • dashboard.json : Grafana 대시보드 구성 파일.

어플리케이션 이해하기

다음은 main.go 에서 찾을 수 있는 어플리케이션의 전체 로직이에요.

package main

import (
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define metrics
var (
	HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_total",
		Help: "Total number of requests processed by the API",
	}, []string{"path", "status"})

	HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_error_total",
		Help: "Total number of errors returned by the API",
	}, []string{"path", "status"})
)

// Custom registry (without default Go metrics)
var customRegistry = prometheus.NewRegistry()

// Register metrics with custom registry
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

func main() {
	router := gin.Default()

	// Register /metrics before middleware
	router.GET("/metrics", PrometheusHandler())
	router.Use(RequestMetricsMiddleware())
	router.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "Up and running!"})
	})
	router.GET("/v1/users", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "Hello from /v1/users"})
	})
	router.Run(":8000")
}

// Custom metrics handler with custom registry
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware to record incoming requests metrics
func RequestMetricsMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path

		c.Next()

		status := c.Writer.Status()
		if status < 400 {
			HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		} else {
			HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		}
	}
}

코드의 이 부분에서 필요한 패키지인 gin , prometheus , promhttp 를 가져왔어요. 그런 다음 몇 가지 변수를 정의했는데, HttpRequestTotal 과 HttpRequestErrorTotal 은 Prometheus 카운터 메트릭이고, customRegistry 는 이 메트릭을 등록하는 데 사용할 커스텀 레지스트리예요. 메트릭의 이름은 메트릭을 식별하는 데 사용할 수 있는 문자열이에요. help 문자열은 /metrics 엔드포인트를 쿼리해 메트릭을 이해할 때 표시될 문자열이에요. Prometheus 클라이언트가 기본적으로 등록하는 기본 Go 메트릭을 피하기 위해 커스텀 레지스트리를 사용하는 거예요. 그런 다음 init 함수를 사용해 메트릭을 커스텀 레지스트리에 등록해요.

main 함수에서 gin 프레임워크의 새 인스턴스를 만들고 세 개의 라우트를 만들었어요. /health 경로에 있는 헬스 엔드포인트는 {"message": "Up and running!"} 이라는 JSON을 반환하고, /v1/users 엔드포인트는 {"message": "Hello from /v1/users"} 이라는 JSON을 반환하는 것을 볼 수 있어요. 세 번째 라우트는 /metrics 엔드포인트용으로 Prometheus 형식의 메트릭을 반환해요. 그런 다음 RequestMetricsMiddleware 미들웨어가 있는데, API에 대한 모든 요청마다 호출돼요. 상태 코드와 경로 같은 수신 요청 메트릭을 기록해요. 마지막으로 gin 어플리케이션을 포트 8000에서 실행해요.

이제 미들웨어 함수 RequestMetricsMiddleware 이 나와요. 이 함수는 API에 대한 모든 요청마다 호출돼요. 상태 코드가 400보다 작거나 같으면 HttpRequestTotal 카운터(경로와 상태 코드별로 다른 카운터)를 증가시켜요. 상태 코드가 400보다 크면 HttpRequestErrorTotal 카운터(경로와 상태 코드별로 다른 카운터)를 증가시켜요. PrometheusHandler 함수는 /metrics 엔드포인트에 대해 호출될 커스텀 핸들러예요. Prometheus 형식의 메트릭을 반환해요.

이게 어플리케이션의 전체 요지예요. 이제 앱이 메트릭을 올바르게 등록하는지 실행하고 테스트할 차례예요.

어플리케이션 실행

터미널에서 계속 go-prometheus-monitoring 디렉터리 안에 있는지 확인하고 다음 명령을 실행해주세요. go mod tidy 를 실행해 의존성을 설치한 다음 go run main.go 를 실행해 어플리케이션을 빌드·실행하세요. 그런 다음 http://localhost:8000/health 또는 http://localhost:8000/v1/users 를 방문해주세요. {"message": "Up and running!"} 또는 {"message": "Hello from /v1/users"} 출력이 보여야 해요. 이 출력이 보이면 앱이 성공적으로 실행 중인 거예요.

이제 /metrics 엔드포인트에 접근해 어플리케이션의 메트릭을 확인해주세요.

브라우저에서 http://localhost:8000/metrics 를 열어주세요. 다음처럼 보이는 출력이 보여야 해요.

# HELP api_http_request_error_total Total number of errors returned by the API
# TYPE api_http_request_error_total counter
api_http_request_error_total{path="/",status="404"} 1
api_http_request_error_total{path="//v1/users",status="404"} 1
api_http_request_error_total{path="/favicon.ico",status="404"} 1
# HELP api_http_request_total Total number of requests processed by the API
# TYPE api_http_request_total counter
api_http_request_total{path="/health",status="200"} 2
api_http_request_total{path="/v1/users",status="200"} 1

터미널에서 ctrl + c 를 눌러 어플리케이션을 중지해주세요.

참고: 로컬에서 어플리케이션을 실행하고 싶지 않고 Docker 컨테이너에서 실행하고 싶다면, Dockerfile을 만들고 어플리케이션을 컨테이너화하는 다음 페이지로 건너뛰세요.

요약

이 섹션에서 Prometheus에 메트릭을 등록하는 Golang 앱을 만드는 방법을 배웠어요. 미들웨어 함수를 구현해 요청 경로와 상태 코드에 기반해 카운터를 증가시킬 수 있었어요.

Golang 어플리케이션 컨테이너화

컨테이너화는 어플리케이션과 그 의존성을 컨테이너라는 단일 패키지로 묶는 데 도움을 줘요. 이 패키지는 환경을 걱정하지 않고 어떤 플랫폼에서든 실행할 수 있어요. 이 섹션에서는 Docker로 Golang 어플리케이션을 컨테이너화하는 방법을 배워요.

Golang 어플리케이션을 컨테이너화하려면 먼저 Dockerfile을 만들어야 해요. Dockerfile은 컨테이너에서 어플리케이션을 빌드·실행하는 지침을 담고 있어요. 또한 Dockerfile을 만들 때 이미지 크기를 최적화하고 더 안전하게 만드는 다양한 모범 사례를 따를 수 있어요.

Dockerfile 만들기

Golang 어플리케이션의 루트 디렉터리에 Dockerfile 이라는 새 파일을 만들어주세요. Dockerfile은 컨테이너에서 어플리케이션을 빌드·실행하는 지침을 담고 있어요.

다음은 Golang 어플리케이션용 Dockerfile이에요. 이 파일은 go-prometheus-monitoring 디렉터리에서도 찾을 수 있어요.

# Use the official Golang image as the base
FROM golang:1.24-alpine AS builder
# Set environment variables
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
# Set working directory inside the container
WORKDIR /build
# Copy go.mod and go.sum files for dependency installation
COPY go.mod go.sum ./
# Download dependencies
RUN go mod download
# Copy the entire application source
COPY . .
# Build the Go binary
RUN go build -o /app .
# Final lightweight stage
FROM alpine:3.21 AS final
# Copy the compiled binary from the builder stage
COPY --from=builder /app /bin/app
# Expose the application's port
EXPOSE 8000
# Run the application
CMD ["bin/app"]

Dockerfile 이해하기

Dockerfile은 두 개의 스테이지로 구성돼요:

  • 빌드 스테이지: 공식 Golang 이미지를 베이스로 사용하고 필요한 환경 변수를 설정해요. 컨테이너 안의 작업 디렉터리도 설정하고, 의존성 설치를 위해 go.mod 와 go.sum 파일을 복사하며, 의존성을 다운로드하고, 전체 어플리케이션 소스를 복사하고, Go 바이너리를 빌드해요.
    • 빌드 스테이지의 베이스 이미지로 golang:1.24-alpine 이미지를 사용해요. CGO_ENABLED=0 환경 변수는 정적 바이너리 빌드에 유용한 CGO를 비활성화해요. 또한 GOOS 와 GOARCH 환경 변수를 각각 linux 와 amd64 로 설정해 Linux 플랫폼용 바이너리를 빌드해요.
  • 최종 스테이지: 공식 Alpine 이미지를 베이스로 사용하고 빌드 스테이지에서 컴파일된 바이너리를 복사해요. 어플리케이션의 포트도 노출하고 어플리케이션을 실행해요.
    • 최종 스테이지의 베이스 이미지로 alpine:3.21 이미지를 사용해요. 빌드 스테이지의 컴파일된 바이너리를 최종 이미지로 복사하고, EXPOSE 지시문으로 어플리케이션의 포트를 노출하며, CMD 지시문으로 어플리케이션을 실행해요.

멀티 스테이지 빌드 외에도 Dockerfile은 공식 이미지 사용, 작업 디렉터리 설정, 필요한 파일만 최종 이미지로 복사하는 모범 사례를 따르고 있어요. 다른 모범 사례로 Dockerfile을 더 최적화할 수도 있어요.

Docker 이미지 빌드 및 어플리케이션 실행

Dockerfile이 있으면 Docker 이미지를 빌드하고 컨테이너에서 어플리케이션을 실행할 수 있어요.

Docker 이미지를 빌드하려면 터미널에서 다음 명령을 실행해주세요:

$ docker build -t go-api:latest .

이미지를 빌드한 후 다음 명령으로 컨테이너에서 어플리케이션을 실행할 수 있어요:

$ docker run -p 8000:8000 go-api:latest

어플리케이션이 컨테이너 안에서 실행되고 http://localhost:8000 에서 접근할 수 있어요. docker ps 명령으로 실행 중인 컨테이너를 확인할 수도 있어요.

$ docker ps

요약

이 섹션에서 Dockerfile로 Golang 어플리케이션을 컨테이너화하는 방법을 배웠어요. 멀티 스테이지 Dockerfile을 만들어 컨테이너에서 어플리케이션을 빌드·실행했어요. Docker 이미지 크기를 최적화하고 더 안전하게 만드는 모범 사례도 배웠어요.

관련 정보:

  • Dockerfile 참조
  • .dockerignore 파일

Docker Compose로 서비스 연결

Golang 어플리케이션을 컨테이너화했으니 이제 Docker Compose로 서비스를 함께 연결할 거예요. Golang 어플리케이션, Prometheus, Grafana 서비스를 연결해 Prometheus와 Grafana로 Golang 어플리케이션을 모니터링할 거예요.

Docker Compose 파일 만들기

Golang 어플리케이션의 루트 디렉터리에 compose.yml 이라는 새 파일을 만들어주세요. Docker Compose 파일은 여러 서비스를 실행하고 함께 연결하는 지침을 담고 있어요.

다음은 Golang, Prometheus, Grafana를 사용하는 프로젝트용 Docker Compose 파일이에요. 이 파일은 go-prometheus-monitoring 디렉터리에서도 찾을 수 있어요.

services:
  api:
    container_name: go-api
    build:
      context: .
      dockerfile: Dockerfile
    image: go-api:latest
    ports:
      - 8000:8000
    networks:
      - go-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 5
    develop:
      watch:
        - path: .
          action: rebuild
  prometheus:
    container_name: prometheus
    image: prom/prometheus:v2.55.0
    volumes:
      - ./Docker/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - 9090:9090
    networks:
      - go-network
  grafana:
    container_name: grafana
    image: grafana/grafana:11.3.0
    volumes:
      - ./Docker/grafana.yml:/etc/grafana/provisioning/datasources/datasource.yaml
      - grafana-data:/var/lib/grafana
    ports:
      - 3000:3000
    networks:
      - go-network
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=password
volumes:
  grafana-data:
networks:
  go-network:
    driver: bridge

Docker Compose 파일 이해하기

Docker Compose 파일은 세 개의 서비스로 구성돼요:

  • Golang 어플리케이션 서비스: Dockerfile로 Golang 어플리케이션을 빌드하고 컨테이너에서 실행해요. 어플리케이션의 포트 8000 을 노출하고 go-network 네트워크에 연결해요. 또한 어플리케이션의 상태를 모니터링하는 헬스 체크를 정의해요. healthcheck 로 어플리케이션의 상태를 모니터링했어요. 헬스 체크는 30초마다 실행되고 실패하면 5번 재시도해요. 헬스 체크는 curl 명령으로 어플리케이션의 /health 엔드포인트를 확인해요. 헬스 체크 외에도 develop 섹션을 추가해 Docker Compose Watch 기능으로 어플리케이션 소스 코드의 변경을 감시하고 재빌드해요.
  • Prometheus 서비스: 컨테이너에서 Prometheus 서버를 실행해요. 공식 Prometheus 이미지인 prom/prometheus:v2.55.0 을 사용해요. 포트 9090 에서 Prometheus 서버를 노출하고 go-network 네트워크에 연결해요. 또한 프로젝트 루트 디렉터리의 Docker 디렉터리에서 prometheus.yml 파일을 마운트했어요. prometheus.yml 파일에는 Golang 어플리케이션의 메트릭을 스크래핑하는 Prometheus 구성이 들어 있어요. 이렇게 Prometheus 서버를 Golang 어플리케이션에 연결해요.
global:
  scrape_interval: 10s
  evaluation_interval: 10s

scrape_configs:
  - job_name: myapp
    static_configs:
      - targets: ["api:8000"]

prometheus.yml 파일에서 Golang 어플리케이션의 메트릭을 스크래핑하는 myapp 이라는 작업을 정의했어요. targets 필드는 메트릭을 스크래핑할 대상을 지정해요. 이 경우 대상은 포트 8000 에서 실행 중인 Golang 어플리케이션이에요. api 는 Docker Compose 파일에서 Golang 어플리케이션의 서비스 이름이에요. Prometheus 서버는 10초마다 Golang 어플리케이션의 메트릭을 스크래핑해요.

  • Grafana 서비스: 컨테이너에서 Grafana 서버를 실행해요. 공식 Grafana 이미지인 grafana/grafana:11.3.0 을 사용해요. 포트 3000 에서 Grafana 서버를 노출하고 go-network 네트워크에 연결해요. 또한 프로젝트 루트 디렉터리의 Docker 디렉터리에서 grafana.yml 파일을 마운트했어요. grafana.yml 파일에는 Prometheus 데이터 소스를 추가하는 Grafana 구성이 들어 있어요. 이렇게 Grafana 서버를 Prometheus 서버에 연결해요. 환경 변수에서 Grafana 관리자 사용자와 비밀번호를 설정했는데, 이는 Grafana 대시보드에 로그인하는 데 사용돼요.
apiVersion: 1
datasources:
- name: Prometheus (Main)
  type: prometheus
  url: http://prometheus:9090
  isDefault: true

grafana.yml 파일에서 Prometheus (Main) 이라는 Prometheus 데이터 소스를 정의했어요. type 필드는 프로메테우스인 데이터 소스의 타입을 지정해요. url 필드는 메트릭을 가져올 Prometheus 서버의 URL을 지정해요. 이 경우 URL은 http://prometheus:9090 이에요. prometheus 는 Docker Compose 파일에서 Prometheus 서버의 서비스 이름이에요. isDefault 필드는 데이터 소스가 Grafana의 기본 데이터 소스인지 여부를 지정해요.

서비스 외에도 Docker Compose 파일은 Grafana 데이터 영속화용 grafana-data 볼륨과 서비스 연결용 go-network 네트워크를 정의해요. 서비스 연결용 커스텀 네트워크 go-network 를 만들었어요. driver: bridge 필드는 네트워크에 사용할 네트워크 드라이버를 지정해요.

서비스 빌드 및 실행

이제 Docker Compose 파일이 있으니 Docker Compose로 서비스를 빌드하고 함께 실행할 수 있어요.

서비스를 빌드·실행하려면 터미널에서 다음 명령을 실행해주세요:

$ docker compose up

docker compose up 명령은 Docker Compose 파일에 정의된 서비스를 빌드하고 함께 실행해요. 터미널에서 다음처럼 보이는 출력이 보일 거예요:

✔ Network go-prometheus-monitoring_go-network Created 0.0s
✔ Container grafana Created 0.3s
✔ Container go-api Created 0.2s
✔ Container prometheus Created 0.3s
Attaching to go-api, grafana, prometheus
go-api | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
go-api |
go-api | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
go-api | - using env: export GIN_MODE=release
go-api | - using code: gin.SetMode(gin.ReleaseMode)
go-api |
go-api | [GIN-debug] GET /metrics --> main.PrometheusHandler.func1 (3 handlers)
go-api | [GIN-debug] GET /health --> main.main.func1 (4 handlers)
go-api | [GIN-debug] GET /v1/users --> main.main.func2 (4 handlers)
go-api | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
go-api | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
go-api | [GIN-debug] Listening and serving HTTP on :8000
prometheus | ts=2025-03-15T05:57:06.676Z caller=main.go:627 level=info msg="No time or size retention was set so using the default time retention" duration=15d
prometheus | ts=2025-03-15T05:57:06.678Z caller=main.go:671 level=info msg="Starting Prometheus Server" mode=server version="(version=2.55.0, branch=HEAD, revision=91d80252c3e528728b0f88d254dd720f6be07cb8)"
grafana | logger=settings t=2025-03-15T05:57:06.865335506Z level=info msg="Config overridden from command line" arg="default.log.mode=console"
grafana | logger=settings t=2025-03-15T05:57:06.865337131Z level=info msg="Config overridden from Environment variable" var="GF_PATHS_DATA=/var/lib/grafana"
grafana | logger=ngalert.state.manager t=2025-03-15T05:57:07.088956839Z level=info msg="State {. . .}"
grafana | logger=plugin.angulardetectorsprovider.dynamic t=2025-03-15T05:57:07.530317298Z level=info msg="Patterns update finished" duration=440.489125ms

서비스가 실행되면 http://localhost:8000 에서 Golang 어플리케이션, http://localhost:9090/health 에서 Prometheus, http://localhost:3000 에서 Grafana에 접근할 수 있어요. docker ps 명령으로 실행 중인 컨테이너를 확인할 수도 있어요.

$ docker ps

요약

이 섹션에서 Docker Compose로 서비스를 함께 연결하는 방법을 배웠어요. 여러 서비스를 함께 실행하고 네트워크로 연결하는 Docker Compose 파일을 만들었어요. 또한 Docker Compose로 서비스를 빌드·실행하는 방법도 배웠어요.

관련 정보:

  • Docker Compose 개요
  • Compose 파일 참조

어플리케이션 개발

마지막 섹션에서 Docker Compose로 서비스를 연결할 수 있음을 보았어요. 이 섹션에서는 Docker로 Golang 어플리케이션을 개발하는 방법을 배워요. 또한 코드를 변경할 때마다 Docker Compose Watch로 이미지를 재빌드하는 방법도 볼 거예요. 마지막으로 Prometheus를 데이터 소스로 사용해 어플리케이션을 테스트하고 Grafana에서 메트릭을 시각화할 거예요.

어플리케이션 개발

이제 로컬에서 Golang 어플리케이션을 변경하면 그 변경이 컨테이너에 반영돼야겠죠? 이를 위한 한 가지 방법은 코드 변경 후 Docker Compose에서 --build 플래그를 사용하는 거예요. 이 플래그는 compose.yml 파일에 build 지시문이 있는 모든 서비스, 즉 api 서비스(Golang 어플리케이션)를 재빌드해요.

docker compose up --build

하지만 이것이 최선의 접근법은 아니에요. 효율적이지 않아요. 코드를 변경할 때마다 수동으로 재빌드해야 해요. 이는 개발에 좋은 흐름이 아니에요.

더 나은 접근법은 Docker Compose Watch를 사용하는 거예요. compose.yml 파일의 api 서비스 아래에 develop 섹션을 추가했어요. 그래서 핫 리로딩에 가까워요. 코드( path 에 정의된)를 변경할 때마다 이미지를 재빌드해요(또는 액션에 따라 재시작해요). 이렇게 사용할 수 있어요:

services:
  api:
    container_name: go-api
    build:
      context: .
      dockerfile: Dockerfile
    image: go-api:latest
    ports:
      - 8000:8000
    networks:
      - go-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 5
    develop:
      watch:
        - path: .
          action: rebuild

compose.yml 파일에 develop 섹션을 추가한 후 다음 명령으로 개발 서버를 시작할 수 있어요:

$ docker compose watch

이제 main.go 나 프로젝트의 다른 파일을 수정하면 api 서비스가 자동으로 재빌드돼요. 터미널에서 다음 출력이 보일 거예요:

Rebuilding service(s) ["api"] after changes were detected...
[+] Building 8.1s (15/15) FINISHED docker:desktop-linux
=> [api internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 704B 0.0s
=> [api internal] load metadata for docker.io/library/alpine:3.17 1.1s .
=> => exporting manifest list sha256:89ebc86fd51e27c1da440dc20858ff55fe42211a1930c2d51bbdce09f430c7f1 0.0s
=> => naming to docker.io/library/go-api:latest 0.0s
=> => unpacking to docker.io/library/go-api:latest 0.0s
=> [api] resolving provenance for metadata file 0.0s
service(s) ["api"] successfully built

어플리케이션 테스트

이제 어플리케이션이 실행 중이니 Grafana 대시보드로 가서 등록 중인 메트릭을 시각화해주세요. 브라우저를 열고 http://localhost:3000 으로 이동해주세요. Grafana 로그인 페이지가 나타날 거예요. 로그인 자격 증명은 Compose 파일에 제공된 것들이에요.

로그인하면 새 대시보드를 만들 수 있어요. 대시보드를 만드는 동안 기본 데이터 소스가 Prometheus 인 것을 알 수 있어요. 이는 grafana.yml 파일에서 이미 데이터 소스를 구성했기 때문이에요.

다양한 패널로 메트릭을 시각화할 수 있어요. 이 가이드는 Grafana의 세부사항을 다루지 않아요. 더 많은 정보는 Grafana 문서 를 참고할 수 있어요. 서로 다른 엔드포인트의 총 요청 수를 시각화하는 Bar Gauge 패널이 있어요. api_http_request_total 과 api_http_request_error_total 메트릭을 사용해 데이터를 얻었어요.

이 패널은 다른 엔드포인트의 총 요청 수를 시각화해 성공·실패 요청을 비교하기 위해 만들었어요. 좋은 요청 모두 바가 초록색이고, 실패한 요청 모두 바가 빨간색이에요. 또한 요청이 어느 엔드포인트에서 오는지(성공 요청인지 실패 요청인지)도 보여줘요. 이 패널을 사용하려면 클론한 리포지토리에서 dashboard.json 파일을 가져올 수 있어요.

요약

이 가이드의 끝에 도달했어요. Docker로 Golang 어플리케이션을 개발하는 방법을 배웠고, 코드를 변경할 때마다 Docker Compose Watch로 이미지를 재빌드하는 방법을 보았으며, 마지막으로 Prometheus를 데이터 소스로 사용해 어플리케이션을 테스트하고 Grafana에서 메트릭을 시각화했어요.

더 알아보기 (Learn more)

  • Prometheus
  • Grafana
  • Docker Compose