Ver3.0 ๐ Prometheus + Grafana ๋ชจ๋ํฐ๋ง ๊ตฌ์ถ ์๋ฒฝ ๊ฐ์ด๋

๐ Prometheus + Grafana ๋ชจ๋ํฐ๋ง ๊ตฌ์ถ ์๋ฒฝ ๊ฐ์ด๋
์์คํ ์ ์งํค๋ ์ต๊ฐ ๋ชจ๋ํฐ๋ง ๋์ค์ ํจ๊ปํ๋ ์ฌ์
์๋
! ๐ ์ค๋์ DevOps ์์ง๋์ด๋ค์ ํ์ ๋ฌด๊ธฐ์ธ Prometheus์ Grafana๋ฅผ ํ์ฉํ ๋ชจ๋ํฐ๋ง ์์คํ
๊ตฌ์ถ์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ.
์๋ฒ๊ฐ ๊ฐ์๊ธฐ ๋ค์ด๋๊ฑฐ๋, CPU ์ฌ์ฉ๋ฅ ์ด ์น์๊ฑฐ๋, ๋ฉ๋ชจ๋ฆฌ๊ฐ ํฐ์ง ๊ฒ ๊ฐ์ ์ํฉ... ๐ฑ ์ด๋ฐ ์
๋ชฝ ๊ฐ์ ์๊ฐ๋ค์ ๋ฏธ๋ฆฌ ๊ฐ์งํ๊ณ ๋์ํ ์ ์๋ค๋ฉด ์ผ๋ง๋ ์ข์๊น?
๋ฐ๋ก ๊ทธ๊ฒ ๋ชจ๋ํฐ๋ง ์์คํ
์ ํ์ด์ผ! ํนํ Prometheus์ Grafana ์กฐํฉ์ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ํ๊ฒฝ์์ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์คํ์์ค ๋ชจ๋ํฐ๋ง ์๋ฃจ์
์ด์ง. ๐ฏ
๐ฏ ์ Prometheus์ Grafana์ธ๊ฐ?
๋จผ์ ์ด ๋ ์น๊ตฌ๊ฐ ์ ์ด๋ ๊ฒ ์ธ๊ธฐ๊ฐ ๋ง์์ง ์์๋ณผ๊น? ๐ค
์์ฆ ํด๋ผ์ฐ๋ ํ๊ฒฝ์์๋ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ๊ฐ ๋์ธ์์? ์์ญ, ์๋ฐฑ ๊ฐ์ ์ปจํ
์ด๋๊ฐ ๋์์ ๋์๊ฐ๋ ํ๊ฒฝ์์ ์ ํต์ ์ธ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ก๋ ํ๊ณ๊ฐ ์์ด.
Prometheus๋ CNCF(Cloud Native Computing Foundation)์ ์กธ์
ํ๋ก์ ํธ๋ก, Kubernetes์ ์ฐฐ๋ก๊ถํฉ์ด์ผ. ์๊ณ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ณ , Pull ๋ฐฉ์์ผ๋ก ๋ฉํธ๋ฆญ์ ์์งํ๋ ๊ฒ ํน์ง์ด์ง. ๐ช
๐ฅ Prometheus์ ํต์ฌ ๊ฐ์
โข ๋ค์ฐจ์ ๋ฐ์ดํฐ ๋ชจ๋ธ: ๋ ์ด๋ธ ๊ธฐ๋ฐ์ผ๋ก ๋ฉํธ๋ฆญ์ ์ ์ฐํ๊ฒ ๊ด๋ฆฌ
โข ๊ฐ๋ ฅํ ์ฟผ๋ฆฌ ์ธ์ด: PromQL๋ก ๋ณต์กํ ๋ฐ์ดํฐ ๋ถ์ ๊ฐ๋ฅ
โข ์๋น์ค ๋์ค์ปค๋ฒ๋ฆฌ: ๋์ ํ๊ฒฝ์์ ์๋์ผ๋ก ํ๊ฒ ๋ฐ๊ฒฌ
โข ๋
๋ฆฝ์ ์ด์: ์ธ๋ถ ์์กด์ฑ ์์ด ๋จ๋
์คํ ๊ฐ๋ฅ
โข ํ๋ถํ Exporter: ๋ค์ํ ์์คํ
๊ณผ ํตํฉ ๊ฐ๋ฅ
๊ทธ๋ผ Grafana๋? ๐จ
Grafana๋ ๋ฐ์ดํฐ ์๊ฐํ์ ๋ํ์์ด์ผ. Prometheus๊ฐ ์์งํ ๋ฐ์ดํฐ๋ฅผ ์๋ฆ๋ต๊ณ ์ง๊ด์ ์ธ ๋์๋ณด๋๋ก ๋ง๋ค์ด์ฃผ์ง. ์ค์๊ฐ์ผ๋ก ์ฐจํธ๊ฐ ์
๋ฐ์ดํธ๋๋ ๊ฑธ ๋ณด๋ฉด ์ ๋ง ๊ฐ๋์ด์ผ!
๊ฒ๋ค๊ฐ Prometheus๋ฟ๋ง ์๋๋ผ InfluxDB, Elasticsearch, MySQL ๋ฑ ๋ค์ํ ๋ฐ์ดํฐ ์์ค๋ฅผ ์ง์ํด์ ํ์ฅ์ฑ๋ ๋ฐ์ด๋. ๐
๐๏ธ ์ํคํ ์ฒ ์ดํดํ๊ธฐ
๋ณธ๊ฒฉ์ ์ผ๋ก ๊ตฌ์ถํ๊ธฐ ์ ์ ์ ์ฒด ์ํคํ ์ฒ๋ฅผ ์ดํดํ๋ ๊ฒ ์ค์ํด. ๊ฐ ์ปดํฌ๋ํธ๊ฐ ์ด๋ป๊ฒ ์ํธ์์ฉํ๋์ง ์์์ผ ๋ฌธ์ ๊ฐ ์๊ฒผ์ ๋ ๋น ๋ฅด๊ฒ ๋์ํ ์ ์๊ฑฐ๋ ! ๐งฉ
๐ ์ฃผ์ ์ปดํฌ๋ํธ ์ญํ
1. Exporter: ๋ค์ํ ์์คํ
์ ๋ฉํธ๋ฆญ์ Prometheus ํ์์ผ๋ก ๋ณํ
2. Prometheus Server: ๋ฉํธ๋ฆญ ์์ง, ์ ์ฅ, ์ฟผ๋ฆฌ ์ฒ๋ฆฌ์ ํต์ฌ
3. Alertmanager: ์๋ฆผ ๊ท์น ํ๊ฐ ๋ฐ ์๋ฆผ ์ ์ก ๊ด๋ฆฌ
4. Grafana: ์์ง๋ ๋ฐ์ดํฐ๋ฅผ ์๊ฐ์ ์ผ๋ก ํํ
5. Service Discovery: ๋์ ํ๊ฒฝ์์ ๋ชจ๋ํฐ๋ง ๋์ ์๋ ๋ฐ๊ฒฌ
์ด ๊ตฌ์กฐ์ ํต์ฌ์ ๋ถ๋ฆฌ๋ ์ฑ
์์ด์ผ. ๊ฐ ์ปดํฌ๋ํธ๊ฐ ์๊ธฐ ์ญํ ์๋ง ์ง์คํ๊ธฐ ๋๋ฌธ์ ํ์ฅ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ๋ฐ์ด๋์ง. ๐ฏ
Prometheus๋ ๋ฐ์ดํฐ ์์ง๊ณผ ์ ์ฅ์ ์ง์คํ๊ณ , Grafana๋ ์๊ฐํ์ ์ง์คํด. ์ด๋ฐ ๊ตฌ์กฐ ๋๋ถ์ ๊ฐ๊ฐ์ ๋
๋ฆฝ์ ์ผ๋ก ์
๊ทธ๋ ์ด๋ํ๊ฑฐ๋ ๊ต์ฒดํ ์ ์์ด!
โ๏ธ Prometheus ์ค์น ๋ฐ ์ค์
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ค์นํด๋ณผ๊น? ์ฌ๋ฌ ๋ฐฉ๋ฒ์ด ์์ง๋ง ๊ฐ์ฅ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ๋ค์ ์๊ฐํ ๊ฒ! ๐ ๏ธ
Docker๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ง ๊ฐ๋จํด. ํ๊ฒฝ ์ค์ ๊ฑฑ์ ์์ด ๋ฐ๋ก ์์ํ ์ ์๊ฑฐ๋ !
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
restart: unless-stopped
networks:
- monitoring
volumes:
prometheus-data:
networks:
monitoring:
driver: bridge
์ ์ค์ ์์ ์ฃผ๋ชฉํ ์ ๋ค์ด ์์ด! ๐
โข storage.tsdb.retention.time: ๋ฐ์ดํฐ ๋ณด๊ด ๊ธฐ๊ฐ์ 30์ผ๋ก ์ค์ ํ์ด. ํ๋ก๋์
ํ๊ฒฝ์์๋ ๋์คํฌ ์ฉ๋์ ๊ณ ๋ คํด์ ์กฐ์ ํด์ผ ํด.
โข web.enable-lifecycle: API๋ฅผ ํตํด ์ค์ ์ ๋ฆฌ๋ก๋ํ ์ ์๊ฒ ํด์ค. ์ฌ์์ ์์ด ์ค์ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ์ง!
โข volumes: ์ค์ ํ์ผ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์๊ตฌ ์ ์ฅํ๋๋ก ๋ณผ๋ฅจ์ ๋ง์ดํธํ์ด.
prometheus.yml์ Prometheus์ ๋๋์ผ. ์ฌ๊ธฐ์ ๋ชจ๋ ๊ฑธ ์ ์ดํ์ง!
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: 'production'
region: 'ap-northeast-2'
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
rule_files:
- "alerts/*.yml"
scrape_configs:
# Prometheus ์์ฒด ๋ชจ๋ํฐ๋ง
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
labels:
env: 'production'
# Node Exporter - ์๋ฒ ๋ฉํธ๋ฆญ
- job_name: 'node-exporter'
static_configs:
- targets:
- 'node-exporter:9100'
labels:
instance: 'server-01'
env: 'production'
# ์ ํ๋ฆฌ์ผ์ด์
๋ฉํธ๋ฆญ
- job_name: 'application'
metrics_path: '/actuator/prometheus'
static_configs:
- targets:
- 'app-server:8080'
labels:
service: 'api-server'
version: 'v1.2.3'
# Kubernetes ์๋น์ค ๋์ค์ปค๋ฒ๋ฆฌ
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
external_labels: ์ฌ๋ฌ Prometheus ์ธ์คํด์ค๋ฅผ ์ด์ํ ๋ ๊ตฌ๋ถํ๋ ์ฉ๋. Federation ๊ตฌ์ฑ ์ ํ์!
relabel_configs: Kubernetes ํ๊ฒฝ์์ ๋์ ์ผ๋ก ํ๊ฒ์ ๋ฐ๊ฒฌํ๊ณ ๋ ์ด๋ธ์ ์ฌ๊ตฌ์ฑํ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ด์ผ.
ํ๋ก๋์ ํ๊ฒฝ์ด๋ผ๋ฉด Kubernetes + Helm ์กฐํฉ์ด ์ต๊ณ ์ผ!
# Prometheus Community Helm Chart ์ ์ฅ์ ์ถ๊ฐ
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# ๋ค์์คํ์ด์ค ์์ฑ
kubectl create namespace monitoring
# values.yaml ํ์ผ ์ค๋น ํ ์ค์น
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--values custom-values.yaml
# ์ค์น ํ์ธ
kubectl get pods -n monitoring
kubectl get svc -n monitoring
prometheus:
prometheusSpec:
retention: 30d
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 2000m
memory: 4Gi
# ์๋น์ค ๋ชจ๋ํฐ ์๋ ๋ฐ๊ฒฌ
serviceMonitorSelectorNilUsesHelmValues: false
podMonitorSelectorNilUsesHelmValues: false
grafana:
enabled: true
adminPassword: "your-secure-password"
persistence:
enabled: true
size: 10Gi
ingress:
enabled: true
hosts:
- grafana.yourdomain.com
tls:
- secretName: grafana-tls
hosts:
- grafana.yourdomain.com
alertmanager:
enabled: true
config:
global:
resolve_timeout: 5m
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 10s
repeat_interval: 12h
receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alerts'
title: '๐จ {{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
Helm Chart๋ฅผ ์ฌ์ฉํ๋ฉด Prometheus, Grafana, Alertmanager๊ฐ ํ ๋ฒ์ ์ค์น๋ผ. ๊ฒ๋ค๊ฐ Kubernetes ํด๋ฌ์คํฐ ๋ชจ๋ํฐ๋ง์ ํ์ํ ๋ชจ๋ Exporter์ ServiceMonitor๋ ํจ๊ป ์ค์น๋์ง! ๐
kube-prometheus-stack์ ์ ๋ง ๊ฐ๋ ฅํ ํจํค์ง์ผ. ๋ณ๋ ์ค์ ์์ด๋ ๋
ธ๋, ํ๋, ์๋น์ค ๋ฉํธ๋ฆญ์ ์๋์ผ๋ก ์์งํด์ค.
๐ Exporter ์ค์ ํ๊ธฐ
Prometheus๋ Pull ๋ฐฉ์์ด๋ผ ๋ฉํธ๋ฆญ์ ๋ ธ์ถํ๋ Exporter๊ฐ ํ์ํด. ๋ค์ํ ์์คํ ์ ๋ง๋ Exporter๋ฅผ ์ค์นํด๋ณด์! ๐
CPU, ๋ฉ๋ชจ๋ฆฌ, ๋์คํฌ, ๋คํธ์ํฌ ๋ฑ ์์คํ ๋ ๋ฒจ ๋ฉํธ๋ฆญ์ ์์งํ๋ ํ์ Exporter์ผ!
docker run -d \
--name node-exporter \
--net="host" \
--pid="host" \
-v "/:/host:ro,rslave" \
prom/node-exporter:latest \
--path.rootfs=/host
# Node Exporter ๋ค์ด๋ก๋
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xvfz node_exporter-1.7.0.linux-amd64.tar.gz
sudo mv node_exporter-1.7.0.linux-amd64/node_exporter /usr/local/bin/
# systemd ์๋น์ค ํ์ผ ์์ฑ
sudo tee /etc/systemd/system/node_exporter.service << EOF
[Unit]
Description=Node Exporter
After=network.target
[Service]
Type=simple
User=prometheus
ExecStart=/usr/local/bin/node_exporter \
--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/) \
--collector.netclass.ignored-devices=^(veth.*|docker.*|br-.*)$$
[Install]
WantedBy=multi-user.target
EOF
# ์๋น์ค ์์
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
# ํ์ธ
curl http://localhost:9100/metrics
Node Exporter๋ฅผ ์คํํ๋ฉด 9100 ํฌํธ๋ก ๋ฉํธ๋ฆญ์ด ๋
ธ์ถ๋ผ. ๋ธ๋ผ์ฐ์ ๋ curl๋ก ์ ์ํด๋ณด๋ฉด ์์ฒญ๋๊ฒ ๋ง์ ๋ฉํธ๋ฆญ์ด ๋ณด์ผ ๊ฑฐ์ผ! ๐ฒ
node_cpu_seconds_total, node_memory_MemAvailable_bytes, node_disk_io_time_seconds_total ๊ฐ์ ๋ฉํธ๋ฆญ๋ค์ด ์ค์๊ฐ์ผ๋ก ์
๋ฐ์ดํธ๋์ง.
Docker ์ปจํ ์ด๋๋ฅผ ๋ชจ๋ํฐ๋งํ๋ ค๋ฉด cAdvisor๊ฐ ํ์์ผ!
docker run -d \
--name=cadvisor \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:ro \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8080:8080 \
--detach=true \
gcr.io/cadvisor/cadvisor:latest
Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์์๋ฅผ ๋ณด์ฌ์ค๊ฒ!
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
}
management:
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
metrics:
export:
prometheus:
enabled: true
tags:
application: ${spring.application.name}
environment: production
endpoint:
prometheus:
enabled: true
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final Counter orderCounter;
private final Timer orderProcessingTimer;
public OrderService(MeterRegistry registry) {
this.orderCounter = Counter.builder("orders.created")
.description("Total number of orders created")
.tag("type", "online")
.register(registry);
this.orderProcessingTimer = Timer.builder("orders.processing.time")
.description("Order processing time")
.register(registry);
}
public void createOrder(Order order) {
orderProcessingTimer.record(() -> {
// ์ฃผ๋ฌธ ์ฒ๋ฆฌ ๋ก์ง
processOrder(order);
orderCounter.increment();
});
}
}
์ด๋ ๊ฒ ํ๋ฉด /actuator/prometheus ์๋ํฌ์ธํธ๋ก ์ ํ๋ฆฌ์ผ์ด์ ๋ฉํธ๋ฆญ์ด ๋ ธ์ถ๋ผ. ๋น์ฆ๋์ค ๋ก์ง์ ๋ง๋ ์ปค์คํ ๋ฉํธ๋ฆญ์ ์ถ๊ฐํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๋ชจ๋ํฐ๋ง์ด ๊ฐ๋ฅํด! ๐ช
| Exporter | ํฌํธ | ์ฉ๋ | ์ฃผ์ ๋ฉํธ๋ฆญ |
|---|---|---|---|
| Node Exporter | 9100 | ์์คํ ๋ฉํธ๋ฆญ | CPU, ๋ฉ๋ชจ๋ฆฌ, ๋์คํฌ, ๋คํธ์ํฌ |
| cAdvisor | 8080 | ์ปจํ ์ด๋ ๋ฉํธ๋ฆญ | ์ปจํ ์ด๋ ๋ฆฌ์์ค ์ฌ์ฉ๋ |
| MySQL Exporter | 9104 | MySQL ๋ฉํธ๋ฆญ | ์ฟผ๋ฆฌ ์ฑ๋ฅ, ์ปค๋ฅ์ ์ |
| Redis Exporter | 9121 | Redis ๋ฉํธ๋ฆญ | ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋, ํค ํต๊ณ |
| Nginx Exporter | 9113 | ์น์๋ฒ ๋ฉํธ๋ฆญ | ์์ฒญ ์, ์๋ต ์๊ฐ |
| Blackbox Exporter | 9115 | ์๋ํฌ์ธํธ ์ฒดํฌ | HTTP, TCP, ICMP ํ๋ก๋ธ |
๐จ Grafana ์ค์น ๋ฐ ๋์๋ณด๋ ๊ตฌ์ฑ
์ด์ ์์งํ ๋ฐ์ดํฐ๋ฅผ ๋ฉ์ง๊ฒ ์๊ฐํํ ์ฐจ๋ก์ผ! Grafana๋ ์ ๋ง ์ง๊ด์ ์ด๊ณ ๊ฐ๋ ฅํ ๋๊ตฌ์ผ. ๐ฏ
version: '3.8'
services:
prometheus:
# ... (์ด์ ์ค์ )
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=your-secure-password
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=http://grafana.yourdomain.com
- GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-worldmap-panel
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
depends_on:
- prometheus
restart: unless-stopped
networks:
- monitoring
volumes:
prometheus-data:
grafana-data:
networks:
monitoring:
driver: bridge
Grafana๋ฅผ ์คํํ๊ณ http://localhost:3000์ผ๋ก ์ ์ํ๋ฉด ๋ก๊ทธ์ธ ํ๋ฉด์ด ๋์. ๊ธฐ๋ณธ ๊ณ์ ์ admin/admin์ด์ง๋ง, ์ ์ค์ ์์ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ณ๊ฒฝํ์ผ๋ ๊ทธ๊ฑธ ์ฌ์ฉํ๋ฉด ๋ผ! ๐
Grafana์์ ๊ฐ์ฅ ๋จผ์ ํ ์ผ์ Prometheus๋ฅผ ๋ฐ์ดํฐ ์์ค๋ก ์ถ๊ฐํ๋ ๊ฑฐ์ผ. ์๋์ผ๋ก ํ ์๋ ์์ง๋ง, Provisioning์ ์ฌ์ฉํ๋ฉด ์๋ํํ ์ ์์ด!
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true
jsonData:
timeInterval: 15s
queryTimeout: 60s
httpMethod: POST
version: 1
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๋์๋ณด๋๋ฅผ ๋ง๋ค์ด๋ณด์! Grafana์ ์ง๊ฐ๋ ์ฌ๊ธฐ์ ๋ฐํ๋ผ. ๐
์ฒ์๋ถํฐ ๋ง๋ค ์๋ ์์ง๋ง, Grafana Labs์์ ์ ๊ณตํ๋ ์ปค๋ฎค๋ํฐ ๋์๋ณด๋๋ฅผ importํ๋ ๊ฒ ํจ์ฌ ๋น ๋ฅด๊ณ ํจ์จ์ ์ด์ผ!
โข Docker Container & Host Metrics: 10619 - Docker ํ๊ฒฝ ํ์
โข Kubernetes Cluster Monitoring: 7249 - K8s ํด๋ฌ์คํฐ ์ ์ฒด ๋ทฐ
โข Spring Boot Statistics: 12900 - Spring Boot ์ ํ๋ฆฌ์ผ์ด์
โข MySQL Overview: 7362 - MySQL ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
๋์๋ณด๋๋ฅผ importํ๋ ๋ฐฉ๋ฒ์ ๊ฐ๋จํด:
1. Grafana ์ข์ธก ๋ฉ๋ด์์ "+" โ "Import" ํด๋ฆญ
2. ๋์๋ณด๋ ID ์
๋ ฅ (์: 1860)
3. Prometheus ๋ฐ์ดํฐ ์์ค ์ ํ
4. Import ๋ฒํผ ํด๋ฆญ!
์ด์ ์ง์ ํจ๋์ ๋ง๋ค์ด๋ณด์! PromQL์ ์ฌ์ฉํด์ ์ํ๋ ๋ฉํธ๋ฆญ์ ์ฟผ๋ฆฌํ๊ณ ์๊ฐํํ ์ ์์ด. ๐ก
๐ ์ ์ฉํ PromQL ์ฟผ๋ฆฌ ์์
CPU ์ฌ์ฉ๋ฅ (%):
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ (%):
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100๋์คํฌ ์ฌ์ฉ๋ฅ (%):
100 - ((node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100)HTTP ์์ฒญ ์๋ (req/s):
rate(http_requests_total[5m])์๋ต ์๊ฐ 95 ๋ฐฑ๋ถ์์:
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))์๋ฌ์จ (%):
(sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))) * 100
PromQL์ ์ฒ์์ ์ข ์ด๋ ค์ธ ์ ์์ด. ํ์ง๋ง ์ต์ํด์ง๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๋๊ตฌ์ผ! ๐
rate(), increase(), sum(), avg() ๊ฐ์ ํจ์๋ค์ ์กฐํฉํ๋ฉด ๊ฑฐ์ ๋ชจ๋ ๋ฉํธ๋ฆญ์ ์ํ๋ ํํ๋ก ๊ฐ๊ณตํ ์ ์์ง.
2. ์์ ์ผ๊ด์ฑ: ๊ฐ์ ๋ฉํธ๋ฆญ์ ๊ฐ์ ์์ ์ฌ์ฉ
3. ์๊ณ๊ฐ ์ค์ : ์ ์/๊ฒฝ๊ณ /์ํ ๊ตฌ๊ฐ์ ์์์ผ๋ก ํ์
4. ๋ณ์ ํ์ฉ: ํ๊ฒฝ, ์๋ฒ, ์๋น์ค๋ณ๋ก ํํฐ๋ง ๊ฐ๋ฅํ๊ฒ
5. ๋งํฌ ์ฐ๊ฒฐ: ํจ๋ ํด๋ฆญ ์ ์์ธ ๋์๋ณด๋๋ก ์ด๋
Grafana์์๋ ์๋ฆผ์ ์ค์ ํ ์ ์์ด! Alerting ๋ฉ๋ด์์ Alert Rule์ ๋ง๋ค์ด๋ณด์.
{
"alert": "HighCPUUsage",
"expr": "100 - (avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100) > 80",
"for": "5m",
"labels": {
"severity": "warning",
"team": "infrastructure"
},
"annotations": {
"summary": "High CPU usage detected on {{ $labels.instance }}",
"description": "CPU usage is {{ $value }}% for more than 5 minutes"
}
}
์๋ฆผ์ Slack, Email, PagerDuty, Webhook ๋ฑ ๋ค์ํ ์ฑ๋๋ก ๋ณด๋ผ ์ ์์ด. ํ์ ์ปค๋ฎค๋์ผ์ด์ ๋๊ตฌ์ ์ฐ๋ํ๋ฉด ์ ๋ง ํธ๋ฆฌํ์ง! ๐ฑ
โก ๊ณ ๊ธ ์ค์ ๋ฐ ์ต์ ํ
๊ธฐ๋ณธ ๊ตฌ์ถ์ ๋๋ฌ์ด! ์ด์ ํ๋ก๋์ ํ๊ฒฝ์์ ์์ ์ ์ผ๋ก ์ด์ํ๊ธฐ ์ํ ๊ณ ๊ธ ์ค์ ๋ค์ ์์๋ณด์. ๐
Prometheus๋ ์๊ณ์ด ๋ฐ์ดํฐ๋ฅผ ๋ก์ปฌ ๋์คํฌ์ ์ ์ฅํด. ์๊ฐ์ด ์ง๋๋ฉด ๋ฐ์ดํฐ๊ฐ ๊ณ์ ์์ด๋๊น ๊ด๋ฆฌ๊ฐ ํ์ํ์ง!
# ๋ฐ์ดํฐ ๋ณด๊ด ๊ธฐ๊ฐ ์ค์
--storage.tsdb.retention.time=30d
# ์ต๋ ์คํ ๋ฆฌ์ง ํฌ๊ธฐ ์ค์
--storage.tsdb.retention.size=50GB
# ์์ถ ์ค์
--storage.tsdb.min-block-duration=2h
--storage.tsdb.max-block-duration=24h
# WAL ์์ถ ํ์ฑํ
--storage.tsdb.wal-compression
๐ ์คํ ๋ฆฌ์ง ์ฉ๋ ๊ณ์ฐ
๋๋ต์ ์ธ ๊ณ์ฐ ๊ณต์:
ํ์ ์ฉ๋ = ์ํ ์ ร ์ํ ํฌ๊ธฐ ร ๋ณด๊ด ๊ธฐ๊ฐ
์์: 1000๊ฐ ๋ฉํธ๋ฆญ, 15์ด ๊ฐ๊ฒฉ, 30์ผ ๋ณด๊ด
โข ์ด๋น ์ํ: 1000 / 15 = 67๊ฐ
โข ์ผ์ผ ์ํ: 67 ร 86400 = 5,788,800๊ฐ
โข 30์ผ ์ํ: 5,788,800 ร 30 = 173,664,000๊ฐ
โข ์ํ๋น ํฌ๊ธฐ: ์ฝ 1-2 bytes
โข ์ด ์ฉ๋: ์ฝ 173MB ~ 347MB (์์ถ ์ )
โข ์์ถ ํ: ์ฝ 50MB ~ 100MB
์ฌ๋ฌ ๋ฐ์ดํฐ์ผํฐ๋ ํด๋ฌ์คํฐ๋ฅผ ๋ชจ๋ํฐ๋งํ๋ค๋ฉด Federation์ด ๋ต์ด์ผ! ์ค์ Prometheus๊ฐ ์ฌ๋ฌ ํ์ Prometheus์์ ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ ๊ตฌ์กฐ์ง. ๐
scrape_configs:
- job_name: 'federate'
scrape_interval: 30s
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="prometheus"}'
- '{__name__=~"job:.*"}'
- '{__name__=~"node_.*"}'
static_configs:
- targets:
- 'prometheus-dc1:9090'
- 'prometheus-dc2:9090'
- 'prometheus-dc3:9090'
labels:
datacenter: 'global'
๋ณต์กํ PromQL ์ฟผ๋ฆฌ๋ฅผ ๋งค๋ฒ ์คํํ๋ฉด ๋๋ ค์ง ์ ์์ด. Recording Rules๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฏธ๋ฆฌ ๊ณ์ฐํด์ ์ ์ฅํ ์ ์์ง! โก
groups:
- name: node_rules
interval: 30s
rules:
# CPU ์ฌ์ฉ๋ฅ ์ฌ์ ๊ณ์ฐ
- record: instance:node_cpu_utilization:rate5m
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ ์ฌ์ ๊ณ์ฐ
- record: instance:node_memory_utilization:ratio
expr: 1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
# ๋์คํฌ ์ฌ์ฉ๋ฅ ์ฌ์ ๊ณ์ฐ
- record: instance:node_disk_utilization:ratio
expr: 1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})
# ๋คํธ์ํฌ ํธ๋ํฝ ์ฌ์ ๊ณ์ฐ
- record: instance:node_network_receive_bytes:rate5m
expr: rate(node_network_receive_bytes_total[5m])
- record: instance:node_network_transmit_bytes:rate5m
expr: rate(node_network_transmit_bytes_total[5m])
- name: application_rules
interval: 30s
rules:
# HTTP ์์ฒญ ์๋
- record: job:http_requests:rate5m
expr: sum by(job, status) (rate(http_requests_total[5m]))
# ์๋ฌ์จ
- record: job:http_errors:rate5m
expr: sum by(job) (rate(http_requests_total{status=~"5.."}[5m]))
# ํ๊ท ์๋ต ์๊ฐ
- record: job:http_request_duration:avg
expr: |
sum by(job) (rate(http_request_duration_seconds_sum[5m]))
/
sum by(job) (rate(http_request_duration_seconds_count[5m]))
Recording Rules๋ฅผ ์ฌ์ฉํ๋ฉด ๋์๋ณด๋ ๋ก๋ฉ ์๋๊ฐ ์์ฒญ ๋นจ๋ผ์ ธ! ํนํ ๋ณต์กํ ์ง๊ณ ์ฟผ๋ฆฌ๋ ์ฌ๋ฌ ํจ๋์์ ๊ฐ์ ๊ณ์ฐ์ ๋ฐ๋ณตํ ๋ ํจ๊ณผ์ ์ด์ผ. ๐
ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ณด์์ด ์ ๋ง ์ค์ํด! ๊ธฐ๋ณธ ์ค์ ์ผ๋ก๋ ๋๊ตฌ๋ ์ ๊ทผํ ์ ์๊ฑฐ๋ . ๐ฑ
# web.yml ํ์ผ ์์ฑ
basic_auth_users:
admin: $2y$10$hashed_password_here
# Prometheus ์คํ ์ ์ต์
์ถ๊ฐ
--web.config.file=web.yml
# grafana.ini ๋๋ ํ๊ฒฝ๋ณ์
[security]
admin_user = admin
admin_password = your-secure-password
secret_key = your-secret-key-for-signing
[auth]
disable_login_form = false
disable_signout_menu = false
[auth.anonymous]
enabled = false
[auth.basic]
enabled = true
[users]
allow_sign_up = false
allow_org_create = false
[session]
cookie_secure = true
cookie_samesite = strict
- โ ๊ธฐ๋ณธ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ
- โ HTTPS ์ฌ์ฉ (Let's Encrypt ๋ฑ)
- โ ๋ฐฉํ๋ฒฝ ์ค์ (ํ์ํ ํฌํธ๋ง ์คํ)
- โ ์ธ์ฆ ํ์ฑํ
- โ ์ ๊ธฐ์ ์ธ ์ ๋ฐ์ดํธ
- โ ์ ๊ทผ ๋ก๊ทธ ๋ชจ๋ํฐ๋ง
- โ ๋ฏผ๊ฐํ ๋ฉํธ๋ฆญ ํํฐ๋ง
| ํญ๋ชฉ | ๊ธฐ๋ณธ๊ฐ | ๊ถ์ฅ๊ฐ | ์ค๋ช |
|---|---|---|---|
| scrape_interval | 1m | 15s | ๋ฉํธ๋ฆญ ์์ง ์ฃผ๊ธฐ |
| scrape_timeout | 10s | 10s | ์์ง ํ์์์ |
| evaluation_interval | 1m | 15s | ๊ท์น ํ๊ฐ ์ฃผ๊ธฐ |
| retention.time | 15d | 30d | ๋ฐ์ดํฐ ๋ณด๊ด ๊ธฐ๊ฐ |
| query.timeout | 2m | 1m | ์ฟผ๋ฆฌ ํ์์์ |
| query.max-samples | 50000000 | 50000000 | ์ต๋ ์ํ ์ |
๐ก ์ฑ๋ฅ ์ต์ ํ ํ
1. ๋ฉํธ๋ฆญ ํํฐ๋ง: ๋ถํ์ํ ๋ฉํธ๋ฆญ์ ์์งํ์ง ์๊ธฐ
2. Recording Rules ํ์ฉ: ๋ณต์กํ ์ฟผ๋ฆฌ ์ฌ์ ๊ณ์ฐ
3. ์ ์ ํ scrape_interval: ๋๋ฌด ์งง์ผ๋ฉด ๋ถํ ์ฆ๊ฐ
4. ๋ ์ด๋ธ ์ต์ํ: ์นด๋๋๋ฆฌํฐ ํญ๋ฐ ๋ฐฉ์ง
5. ๋ฆฌ์์ค ๋ชจ๋ํฐ๋ง: Prometheus ์์ฒด๋ ๋ชจ๋ํฐ๋ง
6. SSD ์ฌ์ฉ: TSDB๋ I/O ์ง์ฝ์
7. ๋ฉ๋ชจ๋ฆฌ ์ถฉ๋ถํ ํ ๋น: ์ต์ 2GB ์ด์ ๊ถ์ฅ
๐จ ์๋ฆผ ์์คํ ๊ตฌ์ถ
๋ชจ๋ํฐ๋ง์ ๊ฝ์ ์๋ฆผ์ด์ผ! ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ ๋ ์ฆ์ ์์์ผ ๋์ํ ์ ์๊ฑฐ๋ . ๐
global:
resolve_timeout: 5m
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
# ์๋ฆผ ๋ผ์ฐํ
๊ท์น
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 10s
repeat_interval: 12h
receiver: 'default'
routes:
# ์ฌ๊ฐํ ์๋ฆผ์ ์ฆ์ ์ ์ก
- match:
severity: critical
receiver: 'pagerduty'
continue: true
# ๊ฒฝ๊ณ ๋ Slack์ผ๋ก
- match:
severity: warning
receiver: 'slack-warnings'
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ จ์ DBA ํ์ผ๋ก
- match_re:
service: ^(mysql|postgresql|redis)$
receiver: 'dba-team'
# ์๋ฆผ ์์ ์ ์ค์
receivers:
- name: 'default'
slack_configs:
- channel: '#monitoring'
title: '๐ {{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
*Alert:* {{ .Labels.alertname }}
*Severity:* {{ .Labels.severity }}
*Instance:* {{ .Labels.instance }}
*Description:* {{ .Annotations.description }}
{{ end }}
- name: 'slack-warnings'
slack_configs:
- channel: '#alerts-warning'
title: 'โ ๏ธ {{ .GroupLabels.alertname }}'
color: 'warning'
- name: 'pagerduty'
pagerduty_configs:
- service_key: 'YOUR_PAGERDUTY_KEY'
description: '{{ .GroupLabels.alertname }}'
- name: 'dba-team'
email_configs:
- to: 'dba-team@company.com'
from: 'alertmanager@company.com'
smarthost: 'smtp.gmail.com:587'
auth_username: 'alertmanager@company.com'
auth_password: 'your-password'
# ์๋ฆผ ์ต์ ๊ท์น
inhibit_rules:
# ๋
ธ๋ ๋ค์ด ์ ํด๋น ๋
ธ๋์ ๋ค๋ฅธ ์๋ฆผ ์ต์
- source_match:
severity: 'critical'
alertname: 'NodeDown'
target_match:
severity: 'warning'
equal: ['instance']
groups:
- name: infrastructure_alerts
interval: 30s
rules:
# ๋
ธ๋ ๋ค์ด ๊ฐ์ง
- alert: NodeDown
expr: up{job="node-exporter"} == 0
for: 1m
labels:
severity: critical
team: infrastructure
annotations:
summary: "Node {{ $labels.instance }} is down"
description: "{{ $labels.instance }} has been down for more than 1 minute"
# ๋์ CPU ์ฌ์ฉ๋ฅ
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
team: infrastructure
annotations:
summary: "High CPU usage on {{ $labels.instance }}"
description: "CPU usage is {{ $value | humanize }}% for more than 5 minutes"
# ๋์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: warning
team: infrastructure
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is {{ $value | humanize }}%"
# ๋์คํฌ ๊ณต๊ฐ ๋ถ์กฑ
- alert: DiskSpaceLow
expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 85
for: 5m
labels:
severity: warning
team: infrastructure
annotations:
summary: "Low disk space on {{ $labels.instance }}"
description: "Disk usage is {{ $value | humanize }}%"
# ๋์คํฌ ๊ณต๊ฐ ์ฌ๊ฐ
- alert: DiskSpaceCritical
expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 95
for: 1m
labels:
severity: critical
team: infrastructure
annotations:
summary: "Critical disk space on {{ $labels.instance }}"
description: "Disk usage is {{ $value | humanize }}%. Immediate action required!"
- name: application_alerts
interval: 30s
rules:
# ๋์ ์๋ฌ์จ
- alert: HighErrorRate
expr: |
(
sum by(job) (rate(http_requests_total{status=~"5.."}[5m]))
/
sum by(job) (rate(http_requests_total[5m]))
) * 100 > 5
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "High error rate in {{ $labels.job }}"
description: "Error rate is {{ $value | humanize }}%"
# ๋๋ฆฐ ์๋ต ์๊ฐ
- alert: SlowResponseTime
expr: |
histogram_quantile(0.95,
sum by(job, le) (rate(http_request_duration_seconds_bucket[5m]))
) > 1
for: 5m
labels:
severity: warning
team: backend
annotations:
summary: "Slow response time in {{ $labels.job }}"
description: "95th percentile response time is {{ $value | humanize }}s"
# ์๋น์ค ๋ค์ด
- alert: ServiceDown
expr: up{job="application"} == 0
for: 1m
labels:
severity: critical
team: backend
annotations:
summary: "Service {{ $labels.job }} is down"
description: "{{ $labels.instance }} has been down for more than 1 minute"
# ๋์ ์์ฒญ ์
- alert: HighRequestRate
expr: sum by(job) (rate(http_requests_total[5m])) > 1000
for: 10m
labels:
severity: warning
team: backend
annotations:
summary: "High request rate in {{ $labels.job }}"
description: "Request rate is {{ $value | humanize }} req/s"
- name: database_alerts
interval: 30s
rules:
# MySQL ์ฐ๊ฒฐ ์ ๋์
- alert: MySQLHighConnections
expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections * 100 > 80
for: 5m
labels:
severity: warning
team: dba
annotations:
summary: "High MySQL connections on {{ $labels.instance }}"
description: "Connection usage is {{ $value | humanize }}%"
# MySQL ์ฌ๋ก์ฐ ์ฟผ๋ฆฌ
- alert: MySQLSlowQueries
expr: rate(mysql_global_status_slow_queries[5m]) > 0.1
for: 5m
labels:
severity: warning
team: dba
annotations:
summary: "MySQL slow queries detected on {{ $labels.instance }}"
description: "Slow query rate is {{ $value | humanize }} queries/s"
# Redis ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ
- alert: RedisHighMemory
expr: redis_memory_used_bytes / redis_memory_max_bytes * 100 > 90
for: 5m
labels:
severity: warning
team: dba
annotations:
summary: "High Redis memory usage on {{ $labels.instance }}"
description: "Memory usage is {{ $value | humanize }}%"
2. ์ ์ ํ ์๊ณ๊ฐ: ๋๋ฌด ๋ฏผ๊ฐํ๋ฉด ํผ๋ก๋ ์ฆ๊ฐ
3. ๋ช ํํ ์ค๋ช : ๋ฌด์์ด ๋ฌธ์ ์ธ์ง, ์ด๋ป๊ฒ ํด๊ฒฐํ ์ง ํฌํจ
4. ์ฌ๊ฐ๋ ๊ตฌ๋ถ: Critical, Warning, Info ๋ ๋ฒจ ๋ถ๋ฆฌ
5. ๊ทธ๋ฃนํ: ๊ด๋ จ ์๋ฆผ์ ๋ฌถ์ด์ ์ ์ก
6. ์ต์ ๊ท์น: ์ค๋ณต ์๋ฆผ ๋ฐฉ์ง
7. ์์ค์ปฌ๋ ์ด์ : ์๊ฐ ๊ฒฝ๊ณผ์ ๋ฐ๋ผ ์๋ฆผ ๋ ๋ฒจ ์์น
๐ ์ค์ ํ์ฉ ์ฌ๋ก
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋ ์ด์ ์ค์ ์ฌ๋ก๋ฅผ ๋ณด์! ์ค์ ํ๋ก๋์ ํ๊ฒฝ์์ ์ด๋ป๊ฒ ํ์ฉํ๋์ง ์์๋ณผ๊ฒ. ๐ผ
๋๊ท๋ชจ ์ผํ๋ชฐ์ ์ด์ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์. ์ฃผ๋ฌธ, ๊ฒฐ์ , ์ฌ๊ณ ๊ด๋ฆฌ ๋ฑ ์ฌ๋ฌ ๋ง์ดํฌ๋ก์๋น์ค๊ฐ ์์ด.
๐ ๋ชจ๋ํฐ๋ง ๋์
โข ์ฃผ๋ฌธ ์๋น์ค: ์ฃผ๋ฌธ ์์ฑ ์๋, ์คํจ์จ, ์ฒ๋ฆฌ ์๊ฐ
โข ๊ฒฐ์ ์๋น์ค: ๊ฒฐ์ ์ฑ๊ณต๋ฅ , ์๋ต ์๊ฐ, ์ธ๋ถ API ์ํ
โข ์ฌ๊ณ ์๋น์ค: ์ฌ๊ณ ์กฐํ ์๋, ๋์์ฑ ์ด์
โข ์ธํ๋ผ: ์๋ฒ ๋ฆฌ์์ค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฑ๋ฅ, ์บ์ ํํธ์จ
โข ๋น์ฆ๋์ค: ์ค์๊ฐ ๋งค์ถ, ์ ํ์จ, ์ฅ๋ฐ๊ตฌ๋ ์ดํ๋ฅ
// ์ฃผ๋ฌธ ์์ฑ ๋ฉํธ๋ฆญ
order_created_total{status="success"} 1234
order_created_total{status="failed"} 5
order_amount_sum{currency="KRW"} 15000000
// ๊ฒฐ์ ๋ฉํธ๋ฆญ
payment_processed_total{method="card",status="success"} 890
payment_processed_total{method="card",status="failed"} 10
payment_processing_duration_seconds{quantile="0.95"} 0.8
// ์ฌ๊ณ ๋ฉํธ๋ฆญ
inventory_check_total 5678
inventory_low_stock_items 23
inventory_out_of_stock_items 3
SaaS ์๋น์ค๋ ์ฌ์ฉ์ ๊ฒฝํ์ด ์ ๋ง ์ค์ํด. ์๋ต ์๊ฐ, ๊ฐ์ฉ์ฑ, ์๋ฌ์จ์ ์ฒ ์ ํ ๋ชจ๋ํฐ๋งํด์ผ ํด!
# ๊ฐ์ฉ์ฑ (Availability) - ๋ชฉํ: 99.9%
(
sum(rate(http_requests_total{status!~"5.."}[30d]))
/
sum(rate(http_requests_total[30d]))
) * 100
# ์๋ต ์๊ฐ (Latency) - ๋ชฉํ: 95%ile < 200ms
histogram_quantile(0.95,
sum by(le) (rate(http_request_duration_seconds_bucket[5m]))
)
# ์๋ฌ์จ (Error Rate) - ๋ชฉํ: < 0.1%
(
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
) * 100
# ์ฒ๋ฆฌ๋ (Throughput)
sum(rate(http_requests_total[5m]))
SLA ๋ฌ์ฑ๋ฅ
99.95%
์ง๋ 30์ผ ํ๊ท
Kubernetes ํ๊ฒฝ์ ๋์ ์ด๋ผ ๋ชจ๋ํฐ๋ง์ด ๋ ๋ณต์กํด. ํ๋๊ฐ ๊ณ์ ์์ฑ๋๊ณ ์ญ์ ๋๋๊น!
# ํ๋ ์ํ
sum by(namespace, pod) (kube_pod_status_phase{phase="Running"})
sum by(namespace, pod) (kube_pod_status_phase{phase="Pending"})
sum by(namespace, pod) (kube_pod_status_phase{phase="Failed"})
# ์ปจํ
์ด๋ ์ฌ์์ ํ์
sum by(namespace, pod) (rate(kube_pod_container_status_restarts_total[5m]))
# ๋
ธ๋ ๋ฆฌ์์ค ์ฌ์ฉ๋ฅ
sum by(node) (
node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes
) / sum by(node) (node_memory_MemTotal_bytes) * 100
# ํ๋ ๋ฆฌ์์ค ์ฌ์ฉ๋ฅ
sum by(namespace, pod) (
rate(container_cpu_usage_seconds_total[5m])
)
# HPA ์ํ
kube_horizontalpodautoscaler_status_current_replicas
kube_horizontalpodautoscaler_status_desired_replicas
# PVC ์ฌ์ฉ๋ฅ
(
kubelet_volume_stats_used_bytes
/
kubelet_volume_stats_capacity_bytes
) * 100
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ DevOps ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์ ์ปจ์คํ ์ ๋ฐ์ผ๋ฉด, ์ด๋ฐ ์ค์ ๋ ธํ์ฐ๋ฅผ ์ง์ ๋ฐฐ์ธ ์ ์์ด! ํนํ ์ฒ์ ๊ตฌ์ถํ ๋๋ ๊ฒฝํ์์ ์กฐ์ธ์ด ์ ๋ง ํฐ ๋์์ด ๋์ง. ๐
๐ ๏ธ ํธ๋ฌ๋ธ์ํ ๊ฐ์ด๋
๋ชจ๋ํฐ๋ง ์์คํ ๋ ๊ฐ๋ ๋ฌธ์ ๊ฐ ์๊ฒจ. ์์ฃผ ๋ฐ์ํ๋ ์ด์๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ดค์ด! ๐ง
โข ๋๋ฌด ๋ง์ ๋ฉํธ๋ฆญ ์์ง
โข ๋์ ์นด๋๋๋ฆฌํฐ (๋ ์ด๋ธ ์กฐํฉ์ด ๋๋ฌด ๋ง์)
โข ๊ธด ๋ณด๊ด ๊ธฐ๊ฐ
ํด๊ฒฐ์ฑ :
โข ๋ถํ์ํ ๋ฉํธ๋ฆญ ํํฐ๋ง
โข ๋ ์ด๋ธ ์ ์ค์ด๊ธฐ
โข retention ๊ธฐ๊ฐ ๋จ์ถ
โข ๋ฉ๋ชจ๋ฆฌ ์ฆ์ค ๋๋ Federation ๊ตฌ์ฑ
scrape_configs:
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
metric_relabel_configs:
# ํน์ ๋ฉํธ๋ฆญ๋ง ์์ง
- source_labels: [__name__]
regex: 'node_(cpu|memory|disk|network).*'
action: keep
# ๋ถํ์ํ ๋ ์ด๋ธ ์ ๊ฑฐ
- regex: 'job'
action: labeldrop
โข ๋ณต์กํ PromQL ์ฟผ๋ฆฌ
โข ๋๋ฌด ๊ธด ์๊ฐ ๋ฒ์
โข ๋ง์ ํจ๋ ์
ํด๊ฒฐ์ฑ :
โข Recording Rules ์ฌ์ฉ
โข ์ฟผ๋ฆฌ ์ต์ ํ
โข ํจ๋ ์ ์ค์ด๊ธฐ
โข ์บ์ฑ ํ์ฑํ
โ Exporter๊ฐ ์ ์ ์คํ ์ค์ธ๊ฐ?
โ ๋ฐฉํ๋ฒฝ์ด ํฌํธ๋ฅผ ๋ง๊ณ ์์ง ์์๊ฐ?
โ Prometheus ์ค์ ์ ํ๊ฒ์ด ์ถ๊ฐ๋์๋?
โ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ์ ์์ธ๊ฐ?
โ ์ธ์ฆ ์ค์ ์ด ์ฌ๋ฐ๋ฅธ๊ฐ?
# Exporter ์ํ ํ์ธ
curl http://localhost:9100/metrics
# Prometheus ํ๊ฒ ์ํ ํ์ธ
curl http://localhost:9090/api/v1/targets
# ํน์ ๋ฉํธ๋ฆญ ์ฟผ๋ฆฌ
curl 'http://localhost:9090/api/v1/query?query=up'
# Prometheus ์ค์ ๊ฒ์ฆ
promtool check config prometheus.yml
# ์๋ฆผ ๊ท์น ๊ฒ์ฆ
promtool check rules alert_rules.yml
โข ์๊ณ๊ฐ ์ฌ์กฐ์
โข for ๊ธฐ๊ฐ ๋๋ฆฌ๊ธฐ
โข ๊ทธ๋ฃนํ ์ค์ ๊ฐ์
โข ์ต์ ๊ท์น ์ถ๊ฐ
โข ๋ถํ์ํ ์๋ฆผ ์ ๊ฑฐ
Prometheus ์์ฒด ๋ฉํธ๋ฆญ์ ํ์ฉํด์ ์ฑ๋ฅ์ ๋ชจ๋ํฐ๋งํ ์ ์์ด:
prometheus_tsdb_head_series - ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋๋ ์๊ณ์ด ์prometheus_tsdb_head_chunks - ๋ฉ๋ชจ๋ฆฌ ์ฒญํฌ ์prometheus_engine_query_duration_seconds - ์ฟผ๋ฆฌ ์คํ ์๊ฐprometheus_rule_evaluation_duration_seconds - ๊ท์น ํ๊ฐ ์๊ฐprometheus_target_scrapes_exceeded_sample_limit_total - ์ํ ์ ํ ์ด๊ณผ
๐ ๋ค์ ๋จ๊ณ์ ๊ณ ๊ธ ์ฃผ์
๊ธฐ๋ณธ ๊ตฌ์ถ์ ๋ง์ณค๋ค๋ฉด ์ด์ ๋ ๊น์ด ๋ค์ด๊ฐ๋ณผ ์ฐจ๋ก์ผ! ๐ฏ
๐ ํ์ต ๋ก๋๋งต
1. Thanos ๋๋ Cortex: ์ฅ๊ธฐ ์คํ ๋ฆฌ์ง์ ๊ณ ๊ฐ์ฉ์ฑ
2. Loki: ๋ก๊ทธ ์ง๊ณ ์์คํ
(Prometheus for logs)
3. Tempo: ๋ถ์ฐ ์ถ์ ์์คํ
4. OpenTelemetry: ํตํฉ ๊ด์ธก์ฑ ํ๋ ์์ํฌ
5. Service Mesh ๋ชจ๋ํฐ๋ง: Istio, Linkerd ๋ฉํธ๋ฆญ
6. PromQL ๊ณ ๊ธ ๊ธฐ๋ฒ: ๋ณต์กํ ์ฟผ๋ฆฌ ์์ฑ
7. ์ปค์คํ
Exporter ๊ฐ๋ฐ: ํนํ๋ ๋ฉํธ๋ฆญ ์์ง
8. GitOps ํตํฉ: ๋ชจ๋ํฐ๋ง as Code
Prometheus๋ ๋จ์ผ ์๋ฒ๋ผ ํ์ฅ์ฑ์ ํ๊ณ๊ฐ ์์ด. Thanos๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ Prometheus๋ฅผ ํตํฉํ๊ณ ์ฅ๊ธฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์์ง!
# Prometheus with Thanos Sidecar
prometheus:
--storage.tsdb.min-block-duration=2h
--storage.tsdb.max-block-duration=2h
thanos-sidecar:
--prometheus.url=http://localhost:9090
--tsdb.path=/prometheus
--objstore.config-file=/etc/thanos/bucket.yml
# Thanos Query - ํตํฉ ์ฟผ๋ฆฌ ์ธํฐํ์ด์ค
thanos-query:
--http-address=0.0.0.0:9090
--store=thanos-sidecar-1:10901
--store=thanos-sidecar-2:10901
--store=thanos-store:10901
# Thanos Store - ์ฅ๊ธฐ ์คํ ๋ฆฌ์ง ๊ฒ์ดํธ์จ์ด
thanos-store:
--data-dir=/var/thanos/store
--objstore.config-file=/etc/thanos/bucket.yml
# Thanos Compactor - ๋ฐ์ดํฐ ์์ถ ๋ฐ ๋ค์ด์ํ๋ง
thanos-compactor:
--data-dir=/var/thanos/compact
--objstore.config-file=/etc/thanos/bucket.yml
--retention.resolution-raw=30d
--retention.resolution-5m=90d
--retention.resolution-1h=1y
๋ฉํธ๋ฆญ๋ง์ผ๋ก๋ ๋ถ์กฑํด! ๋ก๊ทธ๋ ํจ๊ป ๋ณด๋ฉด ๋ฌธ์ ํด๊ฒฐ์ด ํจ์ฌ ์ฌ์์ ธ. Loki๋ Prometheus์ฒ๋ผ ๋ ์ด๋ธ ๊ธฐ๋ฐ์ผ๋ก ๋ก๊ทธ๋ฅผ ์ธ๋ฑ์ฑํด.
# docker-compose.yml์ ์ถ๊ฐ
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
Grafana์์ Loki๋ฅผ ๋ฐ์ดํฐ ์์ค๋ก ์ถ๊ฐํ๋ฉด ๋ฉํธ๋ฆญ๊ณผ ๋ก๊ทธ๋ฅผ ํ ํ๋ฉด์์ ๋ณผ ์ ์์ด! ์ ๋ง ๊ฐ๋ ฅํ์ง? ๐
2. Logs (๋ก๊ทธ): Loki - ์ ์ผ์ด๋ฌ๋๊ฐ?
3. Traces (์ถ์ ): Tempo/Jaeger - ์ด๋ป๊ฒ ์ ํ๋์๋๊ฐ?
์ด ์ธ ๊ฐ์ง๋ฅผ ๋ชจ๋ ๊ฐ์ถ๋ฉด ์๋ฒฝํ ๊ด์ธก์ฑ์ ํ๋ณดํ ์ ์์ด!
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์์ด. ๐
Prometheus์ Grafana๋ฅผ ํ์ฉํ ๋ชจ๋ํฐ๋ง ์์คํ
๊ตฌ์ถ์ ๋ํด ๊ธฐ์ด๋ถํฐ ๊ณ ๊ธ ์ฃผ์ ๊น์ง ๋ค๋ค๋ดค์ด. ์ด์ ๋น์ ๋ ์์คํ
์ ์งํค๋ ์ํธ์๊ฐ ๋ ์ค๋น๊ฐ ๋์ด! ๐ช
๋ชจ๋ํฐ๋ง์ ํ ๋ฒ ๊ตฌ์ถํ๊ณ ๋์ด ์๋์ผ. ์ง์์ ์ผ๋ก ๊ฐ์ ํ๊ณ ์ต์ ํํด์ผ ํด. ์๋ก์ด ์๋น์ค๊ฐ ์ถ๊ฐ๋๋ฉด ๋ฉํธ๋ฆญ์ ์ถ๊ฐํ๊ณ , ์๋ฆผ ๊ท์น์ ์กฐ์ ํ๊ณ , ๋์๋ณด๋๋ฅผ ์
๋ฐ์ดํธํ๋ ๊ฑฐ์ง.
์ฒ์์ ์ด๋ ค์ธ ์ ์์ง๋ง, ํ๋์ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐ๋ผํ๋ค ๋ณด๋ฉด ์ด๋์ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์ผ! ๐
2. Exporter ์ค์น: ๋ค์ํ ์์คํ ์ ๋ฉํธ๋ฆญ ์์ง
3. PromQL ๋ง์คํฐ: ๊ฐ๋ ฅํ ์ฟผ๋ฆฌ ์ธ์ด ํ์ฉ
4. ๋์๋ณด๋ ๊ตฌ์ฑ: ์ง๊ด์ ์ด๊ณ ์ค์ฉ์ ์ธ ์๊ฐํ
5. ์๋ฆผ ์ค์ : ์๋ฏธ ์๋ ์๋ฆผ์ผ๋ก ์ ์ํ ๋์
6. ์ต์ ํ: ์ฑ๋ฅ๊ณผ ๋ฆฌ์์ค ํจ์จ์ฑ ๊ฐ์
7. ํ์ฅ์ฑ: Federation, Thanos๋ก ์ค์ผ์ผ ์์
8. ํตํฉ ๊ด์ธก์ฑ: Metrics + Logs + Traces
๐ฏ ๋ค์ ์ก์ ์์ดํ
โ
ํ
์คํธ ํ๊ฒฝ์ Prometheus + Grafana ์ค์น
โ
Node Exporter๋ก ์๋ฒ ๋ชจ๋ํฐ๋ง ์์
โ
์ปค๋ฎค๋ํฐ ๋์๋ณด๋ import ํด๋ณด๊ธฐ
โ
์ฒซ ๋ฒ์งธ ์๋ฆผ ๊ท์น ์์ฑ
โ
์ ํ๋ฆฌ์ผ์ด์
์ ๋ฉํธ๋ฆญ ์ถ๊ฐ
โ
ํ๋ก๋์
ํ๊ฒฝ ์ ์ฉ ๊ณํ ์๋ฆฝ
โ
ํ์๋ค๊ณผ ๋์๋ณด๋ ๊ณต์
โ
์ง์์ ์ธ ๊ฐ์ ๊ณผ ์ต์ ํ
๋ชจ๋ํฐ๋ง ์์คํ
๊ตฌ์ถ์ด ์ฒ์์ด๊ฑฐ๋ ๋ ๊น์ด ์๋ ํ์ต์ด ํ์ํ๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ DevOps ์ ๋ฌธ๊ฐ๋ค์ ๋์์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์ ์ ํ์ด์ผ. ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ์ ๋ฌธ๊ฐ๋ค์ด ๋น์ ์ ํ๊ฒฝ์ ์ต์ ํ๋ ์๋ฃจ์
์ ์ ์ํด์ค ๊ฑฐ์ผ! ๐
์ด ๊ธ์ด ๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋ผ! ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ๊ณ , ๊ฒฝํ์ ๊ณต์ ํ๋ฉด์ ํจ๊ป ์ฑ์ฅํ์! ๐ช
Happy Monitoring! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.