Kubernetes 클러스터 구축 가이드 (Ubuntu 22.04, Control-plane 1 + Worker 2)

이 문서는 Ubuntu 22.04(서버 3대)에서 kubeadm + containerd 기반의 쿠버네티스 클러스터를 구축하는 과정을 처음부터 끝까지 순서대로 설명합니다.

  • 구성: 총 3대
    • Control-plane: 1대
    • Worker: 2대
  • 컨테이너 런타임: containerd
  • 설치 방식: kubeadm
  • 네트워크 플러그인: Calico

표기 규칙: 모든 명령은 별도 표기가 없으면 모든 노드(Control-plane/Worker 공통)에서 실행합니다. <PLACEHOLDER>는 실제 값으로 바꿔 입력하세요.


권장 VM 스펙 (Proxmox/KVM 기준)

  • 구성: VM 3대 (Control-plane 1: cp1 10.10.0.100, Worker 2: wk1 10.10.0.101, wk2 10.10.0.102)
  • Control-plane 권장: 4 vCPU, 8–16 GB RAM, 60–100 GB SSD/NVMe
  • Worker 권장(각각): 4 vCPU, 8–16 GB RAM, 100–200 GB SSD/NVMe
  • 가상화 옵션(권장):
    • CPU Type: host
    • QEMU Guest Agent: 활성화
    • Disk Bus: VirtIO SCSI, IO Thread 활성화, SSD Emulation(가능 시)
    • NIC Model: VirtIO (Paravirtualized), 브릿지 vmbr0(고정 IP)
    • MTU: 기본 1500 (특수 네트워크 요구가 없으면 기본 유지)
  • 설치/운영 팁:
    • Ubuntu 22.04 cloud-init 템플릿으로 VM 생성 후 hostname/IP를 사전 지정
    • 아웃바운드 인터넷(apt, 컨테이너 이미지 pull) 가능해야 함
    • 시간 동기화(NTP) 활성화, 디스크 성능은 NVMe/SSD 권장
    • 고가용성 필요 시 Control-plane을 3대로 확장해 etcd 쿼럼 구성

0) 사전 점검 및 고정 값 준비

이 단계는 모든 노드(Control-plane/Worker)에서 수행합니다.

  • 고정 IP와 호스트명(예시)
    • Control-plane: cp1 / 10.10.0.100
    • Worker1: wk1 / 10.10.0.101
    • Worker2: wk2 / 10.10.0.102

각 서버에서 호스트명 설정과 /etc/hosts 매핑을 해둡니다.

  • 각 노드는 자신의 호스트명으로 설정합니다(cp1/wk1/wk2).
  • /etc/hosts 매핑 내용은 세 노드 모두 동일하게 추가/유지합니다.
Copy
sudo hostnamectl set-hostname <HOSTNAME>
cat <<EOF | sudo tee -a /etc/hosts
10.10.0.100   cp1
10.10.0.101  wk1
10.10.0.102  wk2
EOF

시간 동기화가 필요하면 chrony 또는 systemd-timesyncd를 활성화하세요.

Copy
sudo timedatectl set-ntp true
sudo timedatectl status

1) 공통 시스템 준비 (모든 노드)

필수 커널 모듈과 sysctl, 스왑, 방화벽 등을 정리합니다.

Copy
sudo apt update

# 커널 모듈 로드 설정
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 네트워크 관련 sysctl 설정
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

sudo sysctl --system

# 스왑 완전 비활성화 (재부팅 후에도 유지)
sudo sed -i.bak '/\sswap\s/d' /etc/fstab
sudo swapoff -a

# UFW를 사용하지 않는 테스트/내부망 환경이라면 비활성화 (선택)
sudo systemctl stop ufw || true
sudo systemctl disable ufw || true

네트워크/보안 정책에 따라 UFW를 유지할 경우, 하단의 “포트/방화벽 참고”를 확인해 필요한 포트를 허용하세요.


2) containerd 설치 및 설정 (모든 노드)

Copy
sudo apt install -y containerd

# 기본 설정 파일 생성
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml >/dev/null

# cgroup 드라이버를 systemd로 사용 (kubelet과 일치)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# 필요 시 sandbox 이미지(파즈 이미지) 명시 (선택)
# sudo sed -i 's#sandbox_image = ".*"#sandbox_image = "registry.k8s.io/pause:3.9"#' /etc/containerd/config.toml

sudo systemctl enable --now containerd
sudo systemctl restart containerd

3) Kubernetes 저장소 등록 및 패키지 설치 (모든 노드)

공식 APT 저장소(pkgs.k8s.io)를 등록하고 kubelet, kubeadm, kubectl을 설치합니다.

Copy
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | \
  sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | \
  sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt update
sudo apt install -y kubelet kubeadm kubectl

# 자동 업데이트 방지(권장)
sudo apt-mark hold kubelet kubeadm kubectl

버전 트랙(v1.30)은 필요 시 최신 안정 버전으로 교체 가능합니다. 트랙을 바꿀 때는 모든 노드에서 동일하게 적용하세요.


4) Control-plane 초기화 (Control-plane 노드만)

먼저 이미지를 미리 받아두면 최초 초기화 시간이 단축됩니다(선택).

Copy
sudo kubeadm config images pull

클러스터를 초기화합니다. Calico 기본값과 맞추기 위해 --pod-network-cidr=192.168.0.0/16를 사용합니다.

Copy
sudo kubeadm init \
  --apiserver-advertise-address=10.10.0.100 \
  --pod-network-cidr=192.168.0.0/16

성공 시 kubeadm join ... 명령이 출력됩니다. Worker 노드 합류에 사용하므로 복사해 보관하세요.

관리자(kubectl) 설정:

Copy
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

네트워크 플러그인(CNI) 설치: Calico 사용

Copy
# Calico 설치 (안정 릴리스 예: v3.27.x)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/calico.yaml

# 설치 후, 시스템 파드와 노드 상태를 확인
kubectl get pods -n kube-system -w

모든 CoreDNS/Calico 파드가 Running이 되고 Control-plane 노드가 Ready가 되면 다음 단계로 진행합니다.


5) Worker 노드 조인 (각 Worker 노드에서)

Control-plane 초기화 시 출력된 kubeadm join 명령을 각 Worker에서 실행합니다. 예:

Copy
sudo kubeadm join 10.10.0.100:6443 --token <TOKEN> \
  --discovery-token-ca-cert-hash sha256:<CA_CERT_HASH>

토큰이 만료되었거나 잃어버렸다면 Control-plane에서 새 토큰을 생성할 수 있습니다.

Copy
# Control-plane에서
kubeadm token create --print-join-command

6) 클러스터 상태 확인 (Control-plane)

Copy
kubectl get nodes -o wide
kubectl get pods -A

3개 노드(Control-plane 1, Worker 2)가 모두 Ready 상태면 구축이 완료되었습니다.


7) 선택/운영상 팁

  • 여러 NIC/다중 IP 환경에서 노드 IP를 고정하려면(선택):
Copy
echo 'KUBELET_EXTRA_ARGS="--node-ip=<NODE_PRIMARY_IP>"' | sudo tee /etc/default/kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet
  • kubectl 자동완성/alias(선택):
Copy
sudo apt install -y bash-completion
echo 'source /usr/share/bash-completion/bash_completion' >> ~/.bashrc
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
exec $SHELL -l
  • 업그레이드 개요(요약):
    1. Control-plane에서 sudo apt-mark unhold kubeadm && sudo apt install -y kubeadm && sudo kubeadm upgrade plan && sudo kubeadm upgrade apply v1.xx.y
    2. 각 노드에서 kubelet/kubectl 패키지 업데이트 후 systemctl restart kubelet
    3. 완료 후 다시 apt-mark hold ...

8) 포트/방화벽 참고 (UFW/외부 방화벽 사용하는 경우)

  • Control-plane 필요 포트(기본):

    • 6443/tcp (Kubernetes API 서버)
    • 2379-2380/tcp (etcd, Control-plane 내부)
    • 10250/tcp (kubelet API)
    • 10257/tcp (kube-controller-manager)
    • 10259/tcp (kube-scheduler)
  • Worker 필요 포트(기본):

    • 10250/tcp (kubelet API)
    • 30000-32767/tcp (NodePort 서비스)

내부망 테스트라면 UFW를 끄는 편이 간단합니다. 운영환경에서는 최소 허용 정책으로 필요한 포트만 개방하세요.


9) 문제 해결 및 리셋

  • 조인 토큰 재발급: kubeadm token create --print-join-command
  • 네트워크 플러그인 재설치 전 정리(주의): /etc/cni/net.d 백업/삭제 후 재적용
  • 클러스터 리셋(모든 노드, 신중히):
Copy
sudo kubeadm reset -f
sudo systemctl restart containerd
sudo rm -rf ~/.kube
sudo rm -rf /etc/cni/net.d/*

필요 시 재부팅 후 다시 1) 공통 시스템 준비 → 2) containerd → 3) 패키지 설치 → 4) 초기화/조인 순으로 재시도하세요.


클러스터 아키텍처 다이어그램

아래 다이어그램은 3대 서버(컨트롤 플레인 1, 워커 2)로 구성된 클러스터에서 Ingress → Service → Pod 경로와 모니터링 스택(Grafana/Prometheus), Calico CNI의 위치를 개략적으로 보여줍니다.

Copy
graph LR
  subgraph Internet["인터넷"]
    User["사용자"]
    DNS["DNS: flyqa.me / api.flyqa.me"]
  end

  User -->|"HTTPS"| DNS

  subgraph Cluster["Kubernetes Cluster"]
    subgraph IngressNS["ns: ingress-nginx"]
      Ingress["Ingress Controller (NGINX)"]
    end

    subgraph FlyqaNS["ns: flyqa"]
      FEsvc["Service: flyqa-frontend :80"]
      BEsvc["Service: flyqa-backend :3000"]
      FEdep["Deployment: flyqa-frontend"]
      BEdep["Deployment: flyqa-backend"]
    end

    subgraph MonitoringNS["ns: monitoring"]
      Grafana[("Grafana")]
      Prom[("Prometheus")]
      Alert["Alertmanager"]
    end

    subgraph CP["Node: cp1 (Control-plane)"]
      APIServer["kube-apiserver"]
      Scheduler["kube-scheduler"]
      Controller["kube-controller-manager"]
      Etcd[("etcd")] 
    end

    subgraph WK1["Node: wk1 (Worker)"]
      kubelet1["kubelet"]
      Pods1["Pods (FE/BE)"]
    end

    subgraph WK2["Node: wk2 (Worker)"]
      kubelet2["kubelet"]
      Pods2["Pods (FE/BE)"]
    end

    CNI["CNI: Calico (Overlay)"]
  end

  DNS -->|"HTTP(S)"| Ingress
  Ingress -->|"/ -> 80"| FEsvc
  Ingress -->|"/api,/socket.io -> 3000"| BEsvc
  FEsvc --> FEdep
  BEsvc --> BEdep

  Prom -->|"scrape"| kubelet1
  Prom -->|"scrape"| kubelet2
  Grafana --> Prom
  Prom -->|"alerts"| Alert

  CNI -.->|"네트워킹"| WK1
  CNI -.->|"네트워킹"| WK2

간단한 범례: Ingress는 도메인 기반 라우팅을 수행하고, Service는 L4 가상 IP로 파드에 로드밸런싱합니다. Prometheus는 노드/파드 메트릭을 수집하고, Grafana는 이를 시각화합니다. Calico는 노드 간 Pod 네트워킹을 제공합니다.


Fly QA 애플리케이션 배포 가이드 (Kubernetes)

사용 도구 개요와 필요성

  • Helm: Kubernetes 패키지 관리자. 복잡한 리소스를 템플릿(차트)으로 묶어 버전/값 관리와 업그레이드를 쉽게 합니다. 모니터링 스택(kube-prometheus-stack), cert-manager 등 반복적 설치를 표준화합니다.
  • Ingress Controller: L7(HTTP/HTTPS) 트래픽을 클러스터 내부 Service로 라우팅하는 컨트롤러(NGINX, Traefik 등). 도메인 기반 라우팅, TLS 종료, 리버스 프록시 기능을 제공합니다.
  • Grafana: 메트릭/로그/트레이싱 데이터의 시각화 대시보드. Prometheus 데이터를 기반으로 시스템 상태를 직관적으로 모니터링할 수 있습니다.
  • Prometheus: 시계열 메트릭 수집/저장 시스템. Kubernetes 컴포넌트/노드/파드의 성능 지표를 스크랩해 저장합니다.
  • Alertmanager: Prometheus 경보를 집계/라우팅해 Slack/이메일 등으로 알림을 전송합니다.
  • metrics-server: kubectl top과 HPA의 실시간 리소스 사용량(메모리/CPU)을 제공하는 경량 메트릭 어그리게이터.
  • cert-manager(선택): TLS 인증서(예: Let’s Encrypt)를 Kubernetes 네이티브 방식으로 자동 발급/갱신해 Ingress에 적용합니다.

아래는 본 저장소의 Fly QA를 Kubernetes에 배포하는 표준 절차입니다. 프론트엔드는 Vite로 빌드된 정적 파일을 Nginx 기반 컨테이너로 서비스하고, 백엔드는 NestJS(Playwright 포함) 이미지를 배포합니다. Ingress는 도메인 라우팅에 사용합니다.

전제: 개인/조직 컨테이너 레지스트리(예: Docker Hub, GHCR, ECR 등) 접근 권한이 있고, 클러스터에 Ingress Controller(예: NGINX Ingress)가 설치되어 있어야 합니다.


A) 환경 변수 설계

백엔드에서 필요로 하는 주요 환경 변수(예시):

  • PORT (기본 3000)
  • NODE_ENV (production)
  • DATABASE_HOST, DATABASE_PORT, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_NAME
  • REDIS_HOST, REDIS_PORT
  • 프런트엔드에서 API URL 사용 시: VITE_API_URL (예: https://api.flyqa.me)

민감 정보는 Kubernetes Secret으로 관리하고, 나머지는 ConfigMap 또는 Deployment env로 주입합니다.


B) 컨테이너 이미지 빌드/푸시

레지스트리 네임스페이스/리포지토리를 다음과 같이 가정합니다.

  • 백엔드 이미지: <REGISTRY>/<NAMESPACE>/fly-qa-backend:<TAG>
  • 프론트엔드 이미지: <REGISTRY>/<NAMESPACE>/fly-qa-frontend:<TAG>
  1. 백엔드 이미지 (Playwright 런타임 포함 Dockerfile 사용)
Copy
# 저장소 루트에서
docker build -t <REGISTRY>/<NAMESPACE>/fly-qa-backend:<TAG> -f Dockerfile .
docker push <REGISTRY>/<NAMESPACE>/fly-qa-backend:<TAG>
  1. 프론트엔드 이미지 (Vite 정적 빌드 → Nginx 서빙)

아래 Dockerfile 예시를 임시 파일로 생성해 사용합니다(리포지토리에 포함하고 싶다면 apps/frontend/Dockerfile로 추가).

Copy
# apps/frontend용 Dockerfile 예시
FROM node:20-alpine AS build
WORKDIR /app
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* ./
COPY apps/frontend/package.json ./apps/frontend/
COPY shared/types/package.json ./shared/types/
COPY shared/utils/package.json ./shared/utils/
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm --filter @fly-qa/frontend build

FROM nginx:1.27-alpine
COPY --from=build /app/apps/frontend/dist /usr/share/nginx/html
COPY server/nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Nginx 기본 설정 파일 예시(server/nginx/default.conf):

Copy
server {
  listen 80;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;

  location / {
    try_files $uri /index.html;
  }
}

이제 프론트엔드 이미지를 빌드/푸시합니다.

Copy
docker build -t <REGISTRY>/<NAMESPACE>/fly-qa-frontend:<TAG> -f apps/frontend/Dockerfile .
docker push <REGISTRY>/<NAMESPACE>/fly-qa-frontend:<TAG>

C) Kubernetes 매니페스트 예시

네임스페이스 생성(선택):

Copy
kubectl create namespace flyqa
  1. Secret/Config (DB/Redis 자격증명)
Copy
apiVersion: v1
kind: Secret
metadata:
  name: flyqa-secrets
  namespace: flyqa
type: Opaque
data:
  DATABASE_HOST: <base64-encoded>
  DATABASE_PORT: <base64-encoded>
  DATABASE_USERNAME: <base64-encoded>
  DATABASE_PASSWORD: <base64-encoded>
  DATABASE_NAME: <base64-encoded>
  REDIS_HOST: <base64-encoded>
  REDIS_PORT: <base64-encoded>
  1. Backend Deployment/Service
Copy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flyqa-backend
  namespace: flyqa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flyqa-backend
  template:
    metadata:
      labels:
        app: flyqa-backend
    spec:
      containers:
        - name: backend
          image: <REGISTRY>/<NAMESPACE>/fly-qa-backend:<TAG>
          imagePullPolicy: IfNotPresent
          env:
            - name: NODE_ENV
              value: "production"
            - name: PORT
              value: "3000"
          envFrom:
            - secretRef:
                name: flyqa-secrets
          ports:
            - containerPort: 3000
          volumeMounts:
            - name: artifacts
              mountPath: /app/screenshots
            - name: artifacts
              mountPath: /app/videos
            - name: artifacts
              mountPath: /app/reports
      volumes:
        - name: artifacts
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: flyqa-backend
  namespace: flyqa
spec:
  selector:
    app: flyqa-backend
  ports:
    - port: 3000
      targetPort: 3000
      name: http
  1. Frontend Deployment/Service
Copy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flyqa-frontend
  namespace: flyqa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flyqa-frontend
  template:
    metadata:
      labels:
        app: flyqa-frontend
    spec:
      containers:
        - name: frontend
          image: <REGISTRY>/<NAMESPACE>/fly-qa-frontend:<TAG>
          imagePullPolicy: IfNotPresent
          env:
            - name: VITE_API_URL
              value: "https://api.flyqa.me"
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: flyqa-frontend
  namespace: flyqa
spec:
  selector:
    app: flyqa-frontend
  ports:
    - port: 80
      targetPort: 80
      name: http
  1. Ingress (예: flyqa.me, api.flyqa.me)
Copy
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: flyqa-ingress
  namespace: flyqa
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
spec:
  tls:
    - hosts:
        - flyqa.me
        - www.flyqa.me
        - api.flyqa.me
      secretName: flyqa-tls # cert-manager로 발급 시 자동 생성 이름 사용
  rules:
    - host: flyqa.me
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: flyqa-frontend
                port:
                  number: 80
    - host: www.flyqa.me
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: flyqa-frontend
                port:
                  number: 80
    - host: api.flyqa.me
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: flyqa-backend
                port:
                  number: 3000

참고: WebSocket(Socke.IO) 타임아웃을 늘리는 애너테이션을 포함했습니다. 클러스터 Ingress Controller 구성에 따라 조정하세요.


D) 배포 실행

Copy
# 네임스페이스
kubectl create namespace flyqa || true

# 시크릿 생성(예시: 리터럴)
kubectl -n flyqa create secret generic flyqa-secrets \
  --from-literal=DATABASE_HOST=<DB_HOST> \
  --from-literal=DATABASE_PORT=<DB_PORT> \
  --from-literal=DATABASE_USERNAME=<DB_USER> \
  --from-literal=DATABASE_PASSWORD=<DB_PASS> \
  --from-literal=DATABASE_NAME=<DB_NAME> \
  --from-literal=REDIS_HOST=<REDIS_HOST> \
  --from-literal=REDIS_PORT=<REDIS_PORT>

# 매니페스트 적용
kubectl -n flyqa apply -f k8s/

# 확인
kubectl -n flyqa get deploy,svc,ingress,pods

k8s/ 디렉터리에 위 예시들을 파일로 나누어 저장해두면 관리가 편리합니다.


E) 새 버전 배포/롤링 업데이트

  1. 코드 변경 → 버전 태그 지정 → 이미지 빌드/푸시
Copy
export TAG=v1.0.0-$(date +%Y%m%d%H%M)
docker build -t <REGISTRY>/<NAMESPACE>/fly-qa-backend:$TAG -f Dockerfile .
docker push <REGISTRY>/<NAMESPACE>/fly-qa-backend:$TAG

docker build -t <REGISTRY>/<NAMESPACE>/fly-qa-frontend:$TAG -f apps/frontend/Dockerfile .
docker push <REGISTRY>/<NAMESPACE>/fly-qa-frontend:$TAG
  1. Deployment 이미지 태그 갱신(예: kubectl set image)
Copy
kubectl -n flyqa set image deploy/flyqa-backend backend=<REGISTRY>/<NAMESPACE>/fly-qa-backend:$TAG
kubectl -n flyqa set image deploy/flyqa-frontend frontend=<REGISTRY>/<NAMESPACE>/fly-qa-frontend:$TAG
  1. 롤아웃 상태 확인/되돌리기
Copy
kubectl -n flyqa rollout status deploy/flyqa-backend
kubectl -n flyqa rollout status deploy/flyqa-frontend

# 문제 시 되돌리기
kubectl -n flyqa rollout undo deploy/flyqa-backend
kubectl -n flyqa rollout undo deploy/flyqa-frontend

F) 스토리지/아티팩트 보존(선택)

현재 예시는 실행 스크린샷/리포트를 emptyDir로 두어 파드 수명과 함께 사라집니다. 장기 보존이 필요하면 PVC로 교체하세요.

Copy
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: flyqa-artifacts
  namespace: flyqa
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 10Gi
---
# Deployment의 volumes/volumeMounts를 아래처럼 변경
volumes:
  - name: artifacts
    persistentVolumeClaim:
      claimName: flyqa-artifacts

부록) 빠른 실행 요약

  1. 모든 노드: 커널 모듈, sysctl, 스왑 비활성, containerd 설치/설정, kubeadm/kubelet/kubectl 설치
  2. Control-plane: kubeadm init --apiserver-advertise-address=10.10.0.100 --pod-network-cidr=192.168.0.0/16
  3. Control-plane: kubeconfig 설정, Calico 적용
  4. Worker: kubeadm join ... 실행
  5. Control-plane: kubectl get nodes로 Ready 확인

운영 모니터링: Grafana + Prometheus (kube-prometheus-stack)

Helm 차트 kube-prometheus-stack(Prometheus Operator 포함)을 사용해 클러스터 메트릭/알림과 대시보드를 손쉽게 구성합니다. Grafana에 Ingress를 붙여 외부에서 접근합니다.

1) Helm 설치 (관리 단말에서 1회)

Copy
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version

2) Helm 리포지토리 등록/갱신

Copy
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

3) 네임스페이스 생성

Copy
kubectl create namespace monitoring || true

4) values.yaml 예시 작성 (Ingress, TLS, 퍼시스턴스)

아래 내용을 k8s/monitoring/values.yaml로 저장하세요. 도메인과 스토리지 클래스를 환경에 맞게 수정합니다.

Copy
grafana:
  adminUser: admin
  adminPassword: "<SET_STRONG_PASSWORD>"
  persistence:
    enabled: true
    size: 10Gi
    # storageClassName: <YOUR_STORAGE_CLASS>
  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: nginx
    hosts:
      - grafana.flyqa.me
    tls:
      - secretName: grafana-tls
        hosts:
          - grafana.flyqa.me

prometheus:
  prometheusSpec:
    retention: 15d
    storageSpec:
      volumeClaimTemplate:
        spec:
          accessModes: [ "ReadWriteOnce" ]
          resources:
            requests:
              storage: 50Gi
          # storageClassName: <YOUR_STORAGE_CLASS>

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          accessModes: [ "ReadWriteOnce" ]
          resources:
            requests:
              storage: 10Gi
          # storageClassName: <YOUR_STORAGE_CLASS>

TLS 자동 발급을 쓸 경우 cert-manager 설치 후 grafana-tls는 자동 관리되도록 애너테이션/ClusterIssuer를 추가하세요.

5) kube-prometheus-stack 설치

Copy
helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
  -n monitoring -f k8s/monitoring/values.yaml

설치 후 리소스 확인:

Copy
kubectl -n monitoring get pods,svc,ingress

Grafana접속: https://grafana.flyqa.me (admin / <SET_STRONG_PASSWORD>)

6) 기본 대시보드/데이터소스

  • 기본으로 Kubernetes/Node/Pod/Cluster 대시보드가 포함됩니다.
  • 데이터소스는 Prometheus가 자동으로 설정됩니다.
  • 필요 시 Import 기능으로 Grafana.com의 대시보드 ID를 추가하세요.

7) metrics-server 설치 (HPA/오토스케일)

Copy
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 확인
kubectl get apiservices | grep metrics
kubectl top nodes
kubectl top pods -A

클러스터 네트워킹/인증서 이슈로 metrics-server가 실패할 경우, --kubelet-insecure-tls 플래그를 추가한 배포 매니페스트를 사용하세요(운영환경에서는 권장하지 않음).

8) 알림(선택)

Alertmanager 수신자 구성 예시를 values에 추가하면 Slack/Email/PagerDuty 등으로 경보를 보낼 수 있습니다.

Copy
alertmanager:
  config:
    global:
      resolve_timeout: 5m
    route:
      receiver: "slack"
    receivers:
      - name: "slack"
        slack_configs:
          - api_url: "https://hooks.slack.com/services/XXX/YYY/ZZZ"
            channel: "#alerts"

적용:

Copy
helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
  -n monitoring -f k8s/monitoring/values.yaml

9) 접근 점검 체크리스트

  • Grafana Ingress가 200 OK로 응답하는지
  • 기본 대시보드에서 노드/파드 메트릭이 수집되는지
  • kubectl top nodes/pods가 동작하는지(metrics-server)
  • 알림 테스트를 보냈을 때 수신되는지