Zum Hauptinhalt springen

Umami

Umami는 Lumie 서비스의 웹 트래픽을 분석하는 자체 호스팅 웹 분석 플랫폼입니다. 쿠키 없이 동작하며 GDPR 친화적인 설계로 사용자 개인정보를 보호합니다.

배포 구성

  • 네임스페이스: umami
  • 차트: umami v7.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_PASSWORDinfra-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

관련 문서