DigitalOcean에서 GitOps 구축하기 (1) - Kubernetes 클러스터와 Traefik

2025년 12월 04일

devops

# Kubernetes# Traefik# DigitalOcean# GitOps# Ingress

시리즈 소개

이 시리즈에서는 DigitalOcean Kubernetes(DOKS)에서 GitOps 환경을 구축하는 전체 과정을 다룹니다.

시리즈 구성

  1. 왜 GitOps인가?
  2. Kubernetes 클러스터와 Traefik (현재 글)
  3. ExternalDNS - 자동 DNS 레코드 관리
  4. SOPS + age - 시크릿 암호화
  5. ArgoCD - GitOps 배포 자동화

DigitalOcean Kubernetes 클러스터 생성

클러스터 생성 (Console)

  1. DigitalOcean Console에 로그인
  2. Kubernetes 메뉴 선택 → Create Cluster
  3. 설정 입력:
    • Region: Singapore (sgp1) - 한국에서 가까움
    • Version: 최신 stable (예: 1.31.x)
    • Node Pool: Basic nodes, $12/month (1 vCPU, 2GB RAM) x 2~3개
    • Name: 클러스터 이름

DigitalOcean K8s는 Control Plane이 무료입니다. Worker Node 비용만 지불하면 됩니다.

kubeconfig 설정

# doctl CLI 설치 (macOS)
brew install doctl

# DigitalOcean 인증
doctl auth init

# kubeconfig 저장
doctl kubernetes cluster kubeconfig save <cluster-name>

# 연결 확인
kubectl get nodes

Traefik 설치

Traefik이란?

Traefik은 클라우드 네이티브 환경에 최적화된 Edge Router입니다. Kubernetes Ingress Controller로 사용하면 자동 서비스 디스커버리, Let’s Encrypt 인증서 자동 발급 등의 기능을 제공합니다.

클러스터 트래픽 플로우

외부에서 클러스터 내부 서비스로 요청이 전달되는 전체 흐름을 살펴보겠습니다.

Kubernetes Cluster

Internet

DO LB

Traefik

Service

Pod #1

Pod #2

Pod #3

각 단계 설명:

단계 구간 설명
Internet → LB 사용자가 도메인으로 요청. DNS는 LB의 External IP를 가리킴
LB → Traefik DO Load Balancer가 Traefik Service(NodePort)로 트래픽 전달
Traefik → Service IngressRoute 규칙에 따라 적절한 백엔드 Service 선택
Service → Pod kube-proxy가 Service의 Endpoint 중 하나의 Pod로 로드밸런싱

Traefik Service 타입

Traefik을 설치하면 type: LoadBalancer인 Service가 생성됩니다.

$ kubectl get svc -n traefik
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)
traefik   LoadBalancer   10.245.x.x     143.xxx.xxx.xxx   80:30080/TCP,443:30443/TCP

DigitalOcean에서는 이 Service를 감지하고 자동으로 Cloud Load Balancer를 프로비저닝합니다. EXTERNAL-IP가 바로 외부에서 접근 가능한 진입점입니다.

왜 Traefik인가? (vs Istio)

Ingress Controller를 선택할 때 Istio도 고려했지만, 다음 이유로 Traefik을 선택했습니다.

항목 Traefik Istio
리소스 사용량 낮음 (~50MB) 높음 (sidecar당 ~100MB+)
복잡도 단순 Service Mesh 전체 스택
학습 곡선 완만 가파름
기능 범위 Ingress/Routing 특화 Full Service Mesh

Istio를 선택하지 않은 이유:

  • 리소스 비용: Istio는 각 Pod에 Envoy sidecar를 주입하여 메모리 사용량이 크게 증가합니다. 작은 클러스터에서는 부담이 됩니다.
  • 과한 기능: mTLS, 트래픽 미러링 등 Service Mesh 기능이 필요하지 않다면 오버스펙입니다.
  • 운영 복잡도: Control Plane(istiod) 관리, sidecar injection 등 신경 쓸 부분이 많습니다.

단순히 Ingress + TLS 자동화 + 라우팅만 필요하다면 Traefik이 훨씬 경제적입니다.

Helmfile을 통한 설치

Helmfile을 사용하면 여러 Helm 차트를 선언적으로 관리할 수 있습니다.

# helmfile.yaml
repositories:
  - name: traefik
    url: https://helm.traefik.io/traefik

releases:
  - name: traefik
    namespace: traefik
    createNamespace: true
    chart: traefik/traefik
    version: 35.2.0
    values:
      - ./values/traefik/common.yaml
      - ./values/traefik/{{ .Environment.Name }}.yaml

traefik-values.yaml

# values/traefik/common.yaml
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

additionalArguments:
  # Let's Encrypt 인증서 저장소 위치
  - "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json"
  # 연락처 이메일
  - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com"
  # HTTP-01 챌린지 사용
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
  - "--log.level=INFO"

service:
  enabled: true
  type: LoadBalancer

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 300m
    memory: 256Mi

ingressRoute:
  dashboard:
    enabled: true
    matchRule: Host(`traefik.example.com`)
    entryPoints: ["traefik"]

crds:
  install: true

ports:
  web:
    expose:
      default: true
    exposedPort: 80
    port: 8000
    protocol: TCP
  websecure:
    expose:
      default: true
    exposedPort: 443
    port: 8443
    protocol: TCP

설치 실행

# Helmfile 설치
brew install helmfile

# 환경별 배포 (dev 환경)
helmfile -e dev sync

# 또는 특정 릴리스만 배포
helmfile -e dev -l name=traefik sync

설치 확인

# Pod 상태 확인
kubectl get pods -n traefik

# Service 확인 (LoadBalancer External IP)
kubectl get svc -n traefik

# Traefik 버전 확인
kubectl logs -n traefik -l app.kubernetes.io/name=traefik | head -20

Ingress 설정

Kubernetes Ingress 사용

Traefik은 표준 Kubernetes Ingress 리소스를 지원합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    traefik.ingress.kubernetes.io/router.tls: "true"
    traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
    external-dns.alpha.kubernetes.io/hostname: my-app.example.com
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - my-app.example.com
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  name: http

주요 어노테이션:

어노테이션 설명
traefik.ingress.kubernetes.io/router.tls TLS 활성화
traefik.ingress.kubernetes.io/router.tls.certresolver Let’s Encrypt resolver 사용
external-dns.alpha.kubernetes.io/hostname ExternalDNS가 DNS 레코드 자동 생성

TLS/HTTPS 설정

Traefik의 Let’s Encrypt 통합으로 인증서가 자동 발급됩니다.

BackendLetsEncryptTraefikClientBackendLetsEncryptTraefikClientHTTPS 요청 (첫 요청)인증서 요청 (ACME)HTTP-01 ChallengeChallenge 응답인증서 발급요청 전달응답 (TLS 암호화)

HTTP-01 Challenge 흐름:

  1. Traefik이 Let’s Encrypt에 인증서 요청
  2. Let’s Encrypt가 http://<domain>/.well-known/acme-challenge/<token> 접근 시도
  3. Traefik이 올바른 응답 반환
  4. 인증서 발급 완료 → /data/acme.json에 저장

인증서는 만료 30일 전에 자동 갱신됩니다.


다음 글 예고

다음 글에서는 ExternalDNS를 설정하여 Kubernetes 리소스와 DNS 레코드를 자동으로 동기화하는 방법을 다룹니다. Ingress에 어노테이션만 추가하면 Cloudflare나 DigitalOcean DNS에 자동으로 레코드가 생성됩니다.


참고 자료

© 2025, 미나리와 함께 만들었음