사설 이미지 저장소 — 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
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은 노드의 컨테이너 런타임과 직접 통신하는 도구라 맥 본체가 아니라 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 |
디버깅 순서 — 막혔을 때의 루틴
생성 → 목록 확인 → 상세 설정·이벤트 확인 → 앱 로그 → 클러스터 전반 이벤트 순으로 좁혀간다.
kubectl get pod -o wide
kubectl describe pod [name] # 스케줄/이미지/마운트 실패 원인
kubectl logs -f [name] # 앱 자체 문제는 여기서
kubectl get events -A # 클러스터 전반 이벤트
ReplicaSet은 Error/Evicted 상태로 남아 있는 파드도 개수에 포함해 센다. 그래서 10개 중 일부가 죽어 있어도 "replicas 충족"으로 보고 새 파드를 안 만든다. 죽은 파드를 삭제해야 비로소 다른 노드에 새로 분배된다.
- 1편. 아키텍처와 핵심 개념
- 2편. k3s로 클러스터 구성하기
- 3편. 네트워킹 — Service, DNS, MetalLB, Ingress
- 4편. 스토리지 — PV/PVC/StorageClass, OpenEBS
- 5편. Helm으로 애플리케이션 배포하기
- 6편. CI/CD + 모니터링 스택 구축 (현재 글)
'기타 > Kubernetes' 카테고리의 다른 글
| Helm으로 애플리케이션 배포하기 (0) | 2026.05.31 |
|---|---|
| 쿠버네티스 네트워킹 — Service, DNS, MetalLB, Ingress (0) | 2026.05.31 |
| k3s로 클러스터 구성하기 (0) | 2026.05.31 |
| 쿠버네티스란 무엇인가? (0) | 2026.05.31 |