Umami
Umami는 Lumie 서비스의 웹 트래픽을 분석하는 자체 호스팅 웹 분석 플랫폼입니다. 쿠키 없이 동작하며 GDPR 친화적인 설계로 사용자 개인정보를 보호합니다.
배포 구성
- 네임스페이스:
umami - 차트:
umamiv7.1.0 (https://charts.christianhuth.de) - 이미지:
zot.lumie-infra.com/umami-software/umami:postgresql-latest - 복제본: 1개
- 서비스 포트: 3000
- 접근: Teleport 앱 프록시 경유 (직접 Ingress 없음)
아키텍처
ArgoCD Multi-Source 배포
Umami도 ArgoCD Multi-Source 패턴을 사용합니다:
spec:
sources:
# 소스 1: Umami Helm 차트
- repoURL: https://charts.christianhuth.de
chart: umami
targetRevision: 7.1.0
helm:
valueFiles:
- $values/applications/umami/helm-values.yaml
# 소스 2: lumie-infra 저장소 (공통 values 참조)
- repoURL: https://github.com/Lumie-Edu/lumie-infra.git
targetRevision: main
ref: values
path: charts/common
helm:
valueFiles:
- $values/applications/umami/common-values.yaml
# Secret 데이터 무시 설정 (VSO가 관리)
ignoreDifferences:
- group: ''
kind: Secret
name: umami-app-secret
jsonPointers:
- /data
ignoreDifferences 설정은 ArgoCD가 VSO(Vault Secrets Operator)가 관리하는 Secret의 데이터 변경을 드리프트로 감지하지 않도록 합니다.
Helm 설정
이미지 및 기본 설정
fullnameOverride: umami
image:
registry: zot.lumie-infra.com
repository: umami-software/umami
tag: "postgresql-latest"
데이터베이스 설정
내장 PostgreSQL 서브차트를 비활성화하고 공유 infra-db 클러스터를 사용합니다:
# 내장 PostgreSQL 비활성화
postgresql:
enabled: false
# 외부 데이터베이스 설정
externalDatabase:
type: postgresql
hostname: infra-db-rw.infra-db.svc.cluster.local
port: 5432
auth:
database: umami
username: umami
# VSO가 생성한 시크릿에서 DATABASE_URL 참조
database:
existingSecret: umami-db-app
databaseUrlKey: uri
환경 변수 (Hash Salt)
envFrom:
- secretRef:
name: umami-hash-salt # HASH_SALT 키 포함
서비스 및 Ingress
# Ingress 비활성화 — Teleport를 통해서만 접근
ingress:
enabled: false
service:
type: ClusterIP
port: 3000
리소스 설정
resources:
requests:
cpu: 15m
memory: 237Mi
limits:
memory: 400Mi
# 주의: 초기 limit을 너무 작게 설정하면 시작 시 OOMKill 발생
# 실제 사용 메모리보다 충분한 여유를 확보할 것
헬스체크
startupProbe:
enabled: true
httpGet:
path: /api/heartbeat
port: 3000
periodSeconds: 10
failureThreshold: 30 # 최대 300초 시작 대기
livenessProbe:
enabled: true
httpGet:
path: /api/heartbeat
port: 3000
periodSeconds: 10
readinessProbe:
enabled: true
httpGet:
path: /api/heartbeat
port: 3000
periodSeconds: 5
파드 분산 배치
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: umami
topologyKey: kubernetes.io/hostname
Vault 시크릿 구조
Hash Salt 시크릿
# common-values.yaml
vaultStaticSecrets:
- name: umami-vss
path: applications/umami
destination:
name: umami-hash-salt
transformation:
templates:
hash-salt: "{{ .Secrets.UMAMI_HASH_SALT }}"
Vault 경로 secret/applications/umami에 필요한 키:
| 키 | 설명 | 생성된 시크릿 |
|---|---|---|
UMAMI_HASH_SALT | 세션 데이터 해시 솔트 | umami-hash-salt |
데이터베이스 시크릿
공유 인프라 PostgreSQL 비밀번호를 Vault에서 가져와 Umami가 기대하는 형식으로 재구성합니다:
- name: umami-db-app-vss
path: infrastructure/postgresql
destination:
name: umami-db-app
transformation:
templates:
host: infra-db-rw.infra-db.svc.cluster.local
port: "5432"
username: umami
password: "{{ .Secrets.POSTGRES_PASSWORD }}"
dbname: umami
uri: "postgresql://umami:{{ .Secrets.POSTGRES_PASSWORD }}@infra-db-rw.infra-db.svc.cluster.local:5432/umami"
Vault 경로 secret/infrastructure/postgresql에 필요한 키:
| 키 | 설명 |
|---|---|
POSTGRES_PASSWORD | infra-db umami 유저 비밀번호 |
네임스페이스 레이블
Umami 네임스페이스에는 MinIO S3 통합을 위한 레이블이 추가됩니다:
managedNamespaceMetadata:
labels:
goldilocks.fairwinds.com/enabled: 'true'
minio-s3: enabled
접근 방법
Teleport를 통한 접근
# Teleport CLI로 앱 로그인
tsh app login umami
# 앱 URI 확인
tsh app config umami --format=uri
포트 포워딩 (개발/디버깅용)
kubectl port-forward -n umami svc/umami 3000:3000
# http://localhost:3000 에서 접근
운영 확인
파드 상태 확인
kubectl get pods -n umami
kubectl logs -n umami -l app.kubernetes.io/name=umami
헬스체크 직접 확인
kubectl exec -n umami deployment/umami -- \
wget -qO- http://localhost:3000/api/heartbeat
시크릿 동기화 확인
# VaultStaticSecret 상태
kubectl get vaultstaticsecret -n umami
# 생성된 시크릿 키 확인 (값 노출 없이)
kubectl get secret -n umami umami-hash-salt -o jsonpath='{.data}' | python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d.keys()))"
kubectl get secret -n umami umami-db-app -o jsonpath='{.data}' | python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d.keys()))"
문제 해결
시작 시 OOMKill 발생
Umami는 Node.js 기반으로 초기 시작 시 메모리 사용량이 높습니다. 메모리 제한이 너무 낮으면 startupProbe 완료 전에 OOMKill이 발생합니다.
# 최근 OOMKill 이벤트 확인
kubectl get events -n umami --field-selector reason=OOMKilling
# 실제 메모리 사용량 확인
kubectl top pod -n umami
현재 limit은 400Mi로 설정되어 있습니다. 지속 적으로 OOMKill이 발생하면 이 값을 늘려야 합니다.
데이터베이스 연결 실패
# 데이터베이스 URL 시크릿 확인
kubectl get secret -n umami umami-db-app -o jsonpath='{.data.uri}' | base64 -d
# infra-db 연결성 테스트
kubectl run -it --rm pg-test --image=postgres:16 -n umami -- \
psql "postgresql://umami:<password>@infra-db-rw.infra-db.svc.cluster.local:5432/umami"
ArgoCD Secret 드리프트 감지
umami-app-secret이 계속 드리프트로 감지된다면 ignoreDifferences 설정이 올바른지 확인합니다:
argocd app get umami --show-operation
kubectl get application -n argocd umami -o yaml | grep -A10 ignoreDifferences