기타/Kubernetes

CI/CD + 모니터링 스택 구축

algml0703 2026. 5. 31. 22:47
반응형

사설 이미지 저장소 — Harbor

회사 내부에서 이미지를 관리하려면 사설 레지스트리가 필요하다. Harbor는 사용자별 권한 제어, 취약점 스캔 등을 제공하는 CNCF 프로젝트다. Helm으로 설치하며, LoadBalancer 타입으로 노출하고 스토리지는 openebs-hostpath를 연결한다.

helm repo add harbor https://helm.goharbor.io
helm pull harbor/harbor
tar xvfz harbor-1.17.1.tgz && mv harbor harbor-1.17.1 && cd harbor-1.17.1
cp values.yaml my-values.yaml

# my-values.yaml 주요 수정 항목
# expose.type: loadBalancer
# expose.loadBalancer.IP: [MetalLB 할당 가능 IP]  (재시작해도 같은 주소 유지)
# externalURL: https://harbor.example.myweb.io
# persistence ... storageClass: openebs-hostpath
# harborAdminPassword: Harbor12345  (기본 admin / Harbor12345)

kubectl create ns harbor
helm install harbor -f my-values.yaml . -n harbor
기본 StorageClass 전환
Harbor가 PVC를 기본 SC로 잡으려 하므로, openebs-hostpath를 기본으로 바꾸고 local-path의 기본 플래그를 떼주면 충돌이 줄어든다.
kubectl patch storageclass openebs-hostpath -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

이미지 업로드와 인증서 문제

이미지를 태깅해 push하는 흐름은 일반 도커 레지스트리와 같다. 다만 Harbor가 자체 서명 CA로 인증서를 만들기 때문에, 클라이언트(맥·도커·containerd)가 그 CA를 신뢰하지 않아 인증 에러가 거의 반드시 발생한다.

docker tag busybox harbor.example.myweb.io/erp/busybox:0.1
docker login harbor.example.myweb.io
docker push harbor.example.myweb.io/erp/busybox:0.1

에러 메시지는 보통 x509: certificate is not trusted 형태다. 해결은 Harbor의 CA 인증서를 꺼내 클라이언트에 신뢰 등록하는 것이다.

# Harbor CA 인증서 추출
kubectl get secret -n harbor harbor-nginx \
  -o jsonpath="{.data.ca\.crt}" | base64 --decode > harbor-ca.crt

# 노드(containerd)에 CA 등록
sudo mkdir -p /usr/local/share/ca-certificates/harbor/
sudo cp harbor-ca.crt /usr/local/share/ca-certificates/harbor/
sudo update-ca-certificates
sudo systemctl restart containerd
참고 — crictl은 노드 안에서
맥에서 도커/쿠버네티스는 가상머신 안에서 돈다. crictl은 노드의 컨테이너 런타임과 직접 통신하는 도구라 맥 본체가 아니라 VM에 접속해서 써야 의미가 있다.

CI 러너 — GitLab Runner

GitLab Runner를 쿠버네티스 executor로 설치하면, 파이프라인 잡마다 파드를 띄워 빌드한다. Helm으로 설치하며 핵심은 등록 토큰·GitLab URL·RBAC·태그 설정이다.

helm repo add gitlab https://charts.gitlab.io
helm install gitlab-runner gitlab/gitlab-runner \
  --namespace gitlab-runner --create-namespace \
  --set runnerRegistrationToken=<GITLAB_TOKEN> \
  --set gitlabUrl=https://<your-gitlab-url> \
  --set rbac.create=true \
  --set runners.tags=k8s-runner

에러 ① Docker daemon에 연결 못 함 (DinD)

파이프라인에서 Cannot connect to the Docker daemon 에러가 나는 건 보통 DinD(Docker-in-Docker) 서비스 설정 문제다. .gitlab-ci.yml에서 services를 잡 단위로 올바르게 선언하고, DOCKER_HOST와 TLS 설정을 맞추면 해결된다.

variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_TLS_CERTDIR: ""

services:
  - name: docker:28.3.1-dind
    alias: docker
    command: ["--tls=false"]

GitOps 배포 — ArgoCD

ArgoCD는 Git 저장소의 매니페스트를 진실의 원천으로 삼아, 클러스터 상태를 Git과 자동으로 동기화한다. 설치 후 LoadBalancer로 노출하거나 포트포워딩으로 접속한다.

helm repo add argo https://argoproj.github.io/argo-helm
helm pull argo/argo-cd
tar xvfz argo-cd-8.2.5.tgz && mv argo-cd argo-cd-8.2.5 && cd argo-cd-8.2.5
cp values.yaml my-values.yaml
helm install argocd -n argocd -f my-values.yaml . --create-namespace

# 초기 admin 비밀번호 확인
kubectl get secret argocd-initial-admin-secret -n argocd \
  -o jsonpath="{.data.password}" | base64 -d

# 포트포워딩 접속 (id: admin)
kubectl port-forward svc/argocd-server -n argocd 8080:443

모니터링 — Prometheus + Grafana

kube-prometheus-stack 차트는 Prometheus, Grafana, Alertmanager를 한 번에 깔아준다.

kubectl create ns monitoring
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
  -f my-values.yaml -n monitoring

# Grafana 접속 (기본 admin / prom-operator)
kubectl port-forward svc/prometheus-grafana -n monitoring 3000:80

애플리케이션 메트릭 연동 — ServiceMonitor

Prometheus가 우리 앱의 메트릭을 수집하게 하려면 ServiceMonitor 리소스로 "어떤 서비스의 어떤 경로를 긁을지" 알려준다. Spring Boot라면 micrometer-registry-prometheus + Actuator를 붙이고 /actuator/prometheus를 노출한다.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: board-monitor
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: board
  endpoints:
    - port: http
      path: /actuator/prometheus
      interval: 15s

로깅 — Loki vs Prometheus

Prometheus는 메트릭(숫자)을, Loki는 로그(텍스트)를 다룬다. 둘 다 Grafana에서 시각화하지만 역할이 다르다.

항목 Prometheus Loki
목적 메트릭·시계열 로그 수집·인덱싱
데이터 CPU, 메모리, 요청 수, 지연시간 Pod·앱·시스템 로그
인덱싱 metric + label 라벨만 (로그 본문은 미인덱싱)
수집 도구 Node Exporter, cAdvisor Promtail, Fluent Bit 등
쿼리 PromQL LogQL

디버깅 순서 — 막혔을 때의 루틴

Apply → Get → Describe → Logs → Get Events
생성 → 목록 확인 → 상세 설정·이벤트 확인 → 앱 로그 → 클러스터 전반 이벤트 순으로 좁혀간다.
kubectl get pod -o wide
kubectl describe pod [name]      # 스케줄/이미지/마운트 실패 원인
kubectl logs -f [name]           # 앱 자체 문제는 여기서
kubectl get events -A            # 클러스터 전반 이벤트
자주 겪는 함정 — Error 파드가 재생성 안 됨
ReplicaSet은 Error/Evicted 상태로 남아 있는 파드도 개수에 포함해 센다. 그래서 10개 중 일부가 죽어 있어도 "replicas 충족"으로 보고 새 파드를 안 만든다. 죽은 파드를 삭제해야 비로소 다른 노드에 새로 분배된다.
쿠버네티스 실전 시리즈
  1. 1편. 아키텍처와 핵심 개념
  2. 2편. k3s로 클러스터 구성하기
  3. 3편. 네트워킹 — Service, DNS, MetalLB, Ingress
  4. 4편. 스토리지 — PV/PVC/StorageClass, OpenEBS
  5. 5편. Helm으로 애플리케이션 배포하기
  6. 6편. CI/CD + 모니터링 스택 구축 (현재 글)
반응형