본문 바로가기
카테고리 없음

Flux CI/CD란

by SuldenLion 2026. 2. 24.
반응형

Flux: GitOps의 선구자

 

들어가며

Flux는 Kubernetes를 위한 GitOps 도구의 선구자입니다. 2016년 Weaveworks가 개발하여 GitOps라는 개념을 처음 정의했으며, 2019년 CNCF 샌드박스 프로젝트로, 2021년 인큐베이팅 프로젝트로 승격되었습니다. Flux는 Git 저장소를 단일 진실 공급원(Single Source of Truth)으로 사용하여 Kubernetes 클러스터를 자동으로 동기화합니다. "Git에 푸시하면 자동 배포"라는 GitOps의 핵심을 구현한 Flux의 모든 것을 깊이 있게 탐구해봅시다.

 

1. Flux의 본질

1.1 Flux란?

Flux = GitOps for Kubernetes

GitOps 원칙:
1. 선언적 (Declarative)
   - 원하는 상태를 선언
   - Kubernetes YAML

2. 버전 관리 (Versioned)
   - Git이 진실의 원천
   - 모든 변경 이력 추적

3. 자동 적용 (Automated)
   - Git → Cluster 자동 동기화
   - 수동 작업 최소화

4. 지속적 조정 (Reconciled)
   - 실제 상태 vs 원하는 상태
   - Drift 자동 수정

전통적 배포:
Developer → CI → kubectl apply → Cluster
              ↑
         (Push 모델)

GitOps (Flux):
Developer → Git → Flux → Cluster
                   ↑
              (Pull 모델)

핵심 개념:

Source (소스):
- GitRepository: Git 저장소
- HelmRepository: Helm 차트
- Bucket: S3, GCS 등

Kustomization:
- Kubernetes 매니페스트 적용
- Kustomize 지원
- 배포 전략

HelmRelease:
- Helm 차트 배포
- 값 오버라이드
- 업그레이드 전략

ImageRepository:
- 컨테이너 이미지 스캔
- 새 태그 감지

ImagePolicy:
- 이미지 업데이트 정책
- Semantic versioning
- Regex 필터

ImageUpdateAutomation:
- Git에 자동 커밋
- 이미지 태그 업데이트

 

1.2 왜 Flux인가?

장점:

✓ GitOps 네이티브
  - Git이 진실의 원천
  - 선언적 구성
  - 버전 관리

✓ Kubernetes 네이티브
  - CRD 기반
  - kubectl로 관리
  - RBAC 통합

✓ 멀티 테넌시
  - Namespace 격리
  - RBAC 기반 권한
  - 팀별 GitOps

✓ 프로그레시브 배포
  - Canary
  - Blue-Green
  - A/B Testing
  - Flagger 통합

✓ CNCF 프로젝트
  - 커뮤니티 지원
  - 벤더 중립
  - 장기 지원

단점:

✗ Kubernetes 전용
  - VM, 서버리스 불가
  - K8s 필수

✗ 학습 곡선
  - GitOps 개념
  - CRD 이해
  - Kustomize/Helm

✗ Git 중심
  - Git 저장소 필수
  - Git 워크플로우 의존

vs ArgoCD:
- UI: ArgoCD 강력 > Flux 기본
- 멀티 클러스터: ArgoCD 중앙집중 vs Flux 분산
- 복잡도: ArgoCD 높음 > Flux 낮음
- CNCF: 둘 다 인큐베이팅

vs Jenkins/GitLab CI:
- 배포 모델: Flux Pull vs CI/CD Push
- K8s 통합: Flux 네이티브 > CI/CD 플러그인
- Drift 감지: Flux 자동 vs CI/CD 수동
- 범위: Flux K8s 전용 vs CI/CD 범용

선택 기준:
Kubernetes 중심 → Flux
GitOps 도입 → Flux
프로그레시브 배포 → Flux + Flagger
중앙 관리 UI 필요 → ArgoCD
VM 배포 포함 → CI/CD

 

1.3 Flux 아키텍처

Flux 컴포넌트:

┌─────────────────────────────────────┐
│         Git Repository              │
│  ┌───────────────────────────────┐ │
│  │  manifests/                   │ │
│  │  ├─ deployment.yaml           │ │
│  │  ├─ service.yaml              │ │
│  │  └─ ingress.yaml              │ │
│  └───────────────────────────────┘ │
└─────────────────────────────────────┘
              ↓ (Pull)
┌─────────────────────────────────────┐
│      Kubernetes Cluster             │
│                                     │
│  ┌────────────────────────────┐   │
│  │  source-controller         │   │
│  │  - Git 저장소 모니터링     │   │
│  │  - 변경 감지               │   │
│  └────────────────────────────┘   │
│              ↓                      │
│  ┌────────────────────────────┐   │
│  │  kustomize-controller      │   │
│  │  - Kustomization 적용      │   │
│  │  - Reconcile               │   │
│  └────────────────────────────┘   │
│              ↓                      │
│  ┌────────────────────────────┐   │
│  │  helm-controller           │   │
│  │  - HelmRelease 적용        │   │
│  │  - 차트 배포               │   │
│  └────────────────────────────┘   │
│              ↓                      │
│  ┌────────────────────────────┐   │
│  │  notification-controller   │   │
│  │  - Slack, Discord 알림     │   │
│  │  - Webhook                 │   │
│  └────────────────────────────┘   │
│              ↓                      │
│  ┌────────────────────────────┐   │
│  │  image-reflector-controller│   │
│  │  - 이미지 스캔             │   │
│  │  - 새 태그 감지            │   │
│  └────────────────────────────┘   │
│              ↓                      │
│  ┌────────────────────────────┐   │
│  │  image-automation-controller│  │
│  │  - Git 자동 커밋           │   │
│  │  - 이미지 업데이트         │   │
│  └────────────────────────────┘   │
└─────────────────────────────────────┘

Reconciliation Loop:
1. Source-controller: Git 저장소 폴링
2. 변경 감지 → 아티팩트 다운로드
3. Kustomize/Helm-controller: 적용
4. 클러스터 상태 조정
5. 성공/실패 알림
6. 5분마다 반복 (기본값)

 

2. Flux 설치

2.1 Flux CLI 설치

bash
# macOS
brew install fluxcd/tap/flux

# Linux
curl -s https://fluxcd.io/install.sh | sudo bash

# Windows (PowerShell)
choco install flux

# 버전 확인
flux --version

# Kubernetes 클러스터 체크
flux check --pre

# 출력:
# ► checking prerequisites
# ✔ Kubernetes 1.28.0 >=1.25.0-0
# ✔ prerequisites checks passed

 

2.2 GitHub Personal Access Token 생성

bash
# GitHub → Settings → Developer settings → Personal access tokens

# 필요한 권한:
# - repo (전체)
# - workflow (GitHub Actions 사용 시)

# 환경 변수 설정
export GITHUB_TOKEN=<your-token>
export GITHUB_USER=<your-username>
export GITHUB_REPO=<your-repo-name>

 

2.3 Flux 부트스트랩

bash
# GitHub 저장소에 부트스트랩
flux bootstrap github \
  --owner=$GITHUB_USER \
  --repository=$GITHUB_REPO \
  --branch=main \
  --path=./clusters/production \
  --personal

# 실행 내용:
# 1. GitHub 저장소 생성 (없으면)
# 2. Flux 컴포넌트 설치
# 3. SSH 키 생성 및 Deploy Key 등록
# 4. GitRepository 및 Kustomization 생성
# 5. Git에 Flux 매니페스트 커밋

# 상태 확인
flux get sources git

# 출력:
# NAME            REVISION        SUSPENDED  READY
# flux-system     main@sha1:abc   False      True

flux get kustomizations

# 출력:
# NAME            REVISION        SUSPENDED  READY
# flux-system     main@sha1:abc   False      True

# Flux 컴포넌트 확인
kubectl get pods -n flux-system

# 출력:
# NAME                                      READY   STATUS
# source-controller-xxx                     1/1     Running
# kustomize-controller-xxx                  1/1     Running
# helm-controller-xxx                       1/1     Running
# notification-controller-xxx               1/1     Running
# image-reflector-controller-xxx            1/1     Running
# image-automation-controller-xxx           1/1     Running

 

2.4 다른 Git 제공자 (GitLab, Bitbucket)

bash
# GitLab
flux bootstrap gitlab \
  --owner=$GITLAB_USER \
  --repository=$GITLAB_REPO \
  --branch=main \
  --path=./clusters/production \
  --token-auth \
  --personal

# Generic Git
flux bootstrap git \
  --url=ssh://git@gitlab.com/$GITLAB_USER/$GITLAB_REPO \
  --branch=main \
  --path=./clusters/production \
  --private-key-file=./identity

# Azure DevOps
flux bootstrap git \
  --url=https://dev.azure.com/org/project/_git/repo \
  --branch=main \
  --path=./clusters/production \
  --username=git \
  --password=$AZURE_DEVOPS_TOKEN

 

3. 기본 사용법

3.1 GitRepository 생성

yaml
# clusters/production/sources/app-repo.yaml

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: app-repo
  namespace: flux-system
spec:
  interval: 1m          # 1분마다 확인
  url: https://github.com/user/app-repo
  ref:
    branch: main
  secretRef:
    name: github-token  # Private 저장소
  ignore: |
    # .gitignore 형식
    *.md
    docs/

---
# Secret (Private 저장소)
apiVersion: v1
kind: Secret
metadata:
  name: github-token
  namespace: flux-system
type: Opaque
stringData:
  username: git
  password: ghp_xxx  # GitHub Token

 

3.2 Kustomization 생성

yaml
# clusters/production/apps/app-kustomization.yaml

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: app
  namespace: flux-system
spec:
  interval: 5m
  path: ./manifests
  prune: true         # 삭제된 리소스 정리
  sourceRef:
    kind: GitRepository
    name: app-repo
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: app
      namespace: default
  timeout: 2m
  retryInterval: 1m

 

3.3 애플리케이션 매니페스트

yaml
# manifests/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: registry.example.com/myapp:1.0.0
        ports:
        - containerPort: 8080

---
# manifests/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: app
  namespace: default
spec:
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

 

3.4 배포 확인

bash
# Git에 푸시
git add .
git commit -m "Deploy app v1.0.0"
git push

# Flux 동기화 대기 (또는 강제)
flux reconcile source git app-repo
flux reconcile kustomization app

# 상태 확인
flux get sources git
flux get kustomizations

# 로그 확인
flux logs --level=info --follow

# 이벤트 확인
kubectl get events -n flux-system --sort-by='.lastTimestamp'

# 애플리케이션 확인
kubectl get pods -n default
kubectl get svc -n default

 

4. Helm 통합

4.1 HelmRepository

yaml
# clusters/production/sources/helm-repo.yaml

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
  name: bitnami
  namespace: flux-system
spec:
  interval: 30m
  url: https://charts.bitnami.com/bitnami

---
# Private Helm Repository
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
  name: private-charts
  namespace: flux-system
spec:
  interval: 10m
  url: https://charts.example.com
  secretRef:
    name: helm-auth
  type: oci  # OCI Registry

---
apiVersion: v1
kind: Secret
metadata:
  name: helm-auth
  namespace: flux-system
type: Opaque
stringData:
  username: myuser
  password: mypassword

 

4.2 HelmRelease

yaml
# clusters/production/apps/nginx-helm.yaml

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: nginx
  namespace: default
spec:
  interval: 30m
  chart:
    spec:
      chart: nginx
      version: '>=13.0.0 <14.0.0'  # Semantic versioning
      sourceRef:
        kind: HelmRepository
        name: bitnami
        namespace: flux-system
      interval: 12h
  
  values:
    replicaCount: 3
    
    service:
      type: LoadBalancer
      port: 80
    
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
      requests:
        cpu: 100m
        memory: 128Mi
    
    ingress:
      enabled: true
      hostname: app.example.com
      tls: true
  
  # 값 오버라이드 (ConfigMap/Secret)
  valuesFrom:
    - kind: ConfigMap
      name: nginx-values
      valuesKey: values.yaml
    - kind: Secret
      name: nginx-secrets
      valuesKey: secrets.yaml
  
  # 업그레이드 전략
  install:
    remediation:
      retries: 3
  
  upgrade:
    remediation:
      retries: 3
      remediateLastFailure: true
    cleanupOnFail: true
  
  # 롤백
  rollback:
    recreate: true
    force: true
  
  # 테스트
  test:
    enable: true
  
  # 대기 시간
  timeout: 5m

 

4.3 Helm 차트 의존성

yaml
# Chart.yaml

apiVersion: v2
name: myapp
version: 1.0.0
dependencies:
  - name: postgresql
    version: 12.x.x
    repository: https://charts.bitnami.com/bitnami
  - name: redis
    version: 17.x.x
    repository: https://charts.bitnami.com/bitnami

---
# HelmRelease

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: myapp
  namespace: default
spec:
  interval: 30m
  chart:
    spec:
      chart: ./charts/myapp
      sourceRef:
        kind: GitRepository
        name: app-repo
        namespace: flux-system
  
  dependsOn:
    - name: postgresql
      namespace: default
    - name: redis
      namespace: default
  
  values:
    postgresql:
      enabled: true
      auth:
        username: myuser
        password: mypassword
        database: mydb
    
    redis:
      enabled: true
      auth:
        enabled: false

 

5. 이미지 자동 업데이트

5.1 ImageRepository

yaml
# clusters/production/images/image-repo.yaml

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: myapp
  namespace: flux-system
spec:
  image: registry.example.com/myapp
  interval: 1m
  secretRef:
    name: docker-registry-credentials

---
apiVersion: v1
kind: Secret
metadata:
  name: docker-registry-credentials
  namespace: flux-system
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-encoded-docker-config>

 

5.2 ImagePolicy

yaml
# clusters/production/images/image-policy.yaml

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: myapp
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: myapp
  
  # Semantic versioning
  policy:
    semver:
      range: 1.x.x
  
  # 또는 Numerical (최신 태그)
  # policy:
  #   numerical:
  #     order: asc
  
  # 또는 Alphabetical
  # policy:
  #   alphabetical:
  #     order: asc
  
  # 필터 (정규식)
  filterTags:
    pattern: '^main-[a-f0-9]+-(?P<ts>[0-9]+)'
    extract: '$ts'

 

5.3 ImageUpdateAutomation

yaml
# clusters/production/images/image-update.yaml

apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: myapp-update
  namespace: flux-system
spec:
  interval: 1m
  
  sourceRef:
    kind: GitRepository
    name: app-repo
  
  git:
    checkout:
      ref:
        branch: main
    
    commit:
      author:
        name: fluxbot
        email: flux@example.com
      messageTemplate: |
        Update image to {{range .Updated.Images}}{{println .}}{{end}}
    
    push:
      branch: main
  
  update:
    path: ./manifests
    strategy: Setters

---
# manifests/deployment.yaml (마커 추가)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: default
spec:
  template:
    spec:
      containers:
      - name: app
        image: registry.example.com/myapp:1.0.0  # {"$imagepolicy": "flux-system:myapp"}

# Flux가 자동으로 업데이트:
# image: registry.example.com/myapp:1.2.3

 

5.4 이미지 업데이트 확인

bash
# ImageRepository 확인
flux get image repository myapp

# ImagePolicy 확인
flux get image policy myapp

# 최신 이미지 확인
kubectl get imagepolicy myapp -n flux-system -o yaml

# spec.latestImage 확인
# latestImage: registry.example.com/myapp:1.2.3

# Git 커밋 확인
git log --oneline
# abc1234 Update image to registry.example.com/myapp:1.2.3

 

6. 멀티 클러스터 & 멀티 테넌시

6.1 멀티 클러스터 구조

Git Repository:
├── clusters/
│   ├── production/
│   │   ├── flux-system/
│   │   │   ├── gotk-components.yaml
│   │   │   └── gotk-sync.yaml
│   │   └── apps/
│   │       └── app-kustomization.yaml
│   ├── staging/
│   │   ├── flux-system/
│   │   └── apps/
│   └── development/
│       ├── flux-system/
│       └── apps/
└── apps/
    ├── base/
    │   ├── deployment.yaml
    │   ├── service.yaml
    │   └── kustomization.yaml
    └── overlays/
        ├── production/
        │   └── kustomization.yaml
        ├── staging/
        │   └── kustomization.yaml
        └── development/
            └── kustomization.yaml

 

6.2 Base 구성

yaml
# apps/base/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 1  # 오버레이에서 변경
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: registry.example.com/myapp:latest
        resources:
          limits:
            cpu: 100m
            memory: 128Mi

---
# apps/base/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml

 

6.3 Overlay 구성

yaml
# apps/overlays/production/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
namespace: production
replicas:
  - name: app
    count: 5
images:
  - name: registry.example.com/myapp
    newTag: v1.2.3
configMapGenerator:
  - name: app-config
    literals:
      - ENV=production
      - LOG_LEVEL=info

---
# apps/overlays/staging/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
namespace: staging
replicas:
  - name: app
    count: 2
images:
  - name: registry.example.com/myapp
    newTag: v1.2.3-rc.1
configMapGenerator:
  - name: app-config
    literals:
      - ENV=staging
      - LOG_LEVEL=debug

 

6.4 클러스터별 Kustomization

yaml
# clusters/production/apps/app-kustomization.yaml

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: app
  namespace: flux-system
spec:
  interval: 10m
  path: ./apps/overlays/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: app-repo

---
# clusters/staging/apps/app-kustomization.yaml

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: app
  namespace: flux-system
spec:
  interval: 5m
  path: ./apps/overlays/staging
  prune: true
  sourceRef:
    kind: GitRepository
    name: app-repo

 

6.5 멀티 테넌시 (팀별 격리)

yaml
# tenants/team-a/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: team-a

---
# tenants/team-a/git-repo.yaml

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: team-a-repo
  namespace: team-a
spec:
  interval: 1m
  url: https://github.com/org/team-a-repo
  ref:
    branch: main
  secretRef:
    name: team-a-github-token

---
# tenants/team-a/kustomization.yaml

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: team-a-apps
  namespace: team-a
spec:
  interval: 5m
  path: ./apps
  prune: true
  sourceRef:
    kind: GitRepository
    name: team-a-repo
  serviceAccountName: team-a-reconciler

---
# tenants/team-a/rbac.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: team-a-reconciler
  namespace: team-a

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-reconciler
  namespace: team-a
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin  # 또는 제한된 Role
subjects:
  - kind: ServiceAccount
    name: team-a-reconciler
    namespace: team-a

 

7. 프로그레시브 배포 (Flagger)

7.1 Flagger 설치

bash
# HelmRepository 추가
flux create source helm flagger \
  --url=https://flagger.app \
  --namespace=flux-system

# Flagger 설치
flux create helmrelease flagger \
  --source=HelmRepository/flagger \
  --chart=flagger \
  --namespace=flux-system \
  --values=- <<EOF
metricsServer: http://prometheus:9090
meshProvider: istio
EOF

# 또는 YAML
cat <<EOF | kubectl apply -f -
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
  name: flagger
  namespace: flux-system
spec:
  interval: 30m
  url: https://flagger.app

---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: flagger
  namespace: flux-system
spec:
  interval: 30m
  chart:
    spec:
      chart: flagger
      sourceRef:
        kind: HelmRepository
        name: flagger
  values:
    metricsServer: http://prometheus:9090
    meshProvider: istio
EOF

 

7.2 Canary 배포

yaml
# apps/canary.yaml

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: myapp
  namespace: default
spec:
  # 대상 Deployment
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  
  # 서비스
  service:
    port: 80
    targetPort: 8080
  
  # 분석 (메트릭 기반)
  analysis:
    # 체크 간격
    interval: 1m
    
    # 최대 웨이트 (점진적 증가)
    maxWeight: 50
    
    # 증가 단계
    stepWeight: 5
    
    # 성공 임계값
    threshold: 5
    
    # 메트릭
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m
      
      - name: request-duration
        thresholdRange:
          max: 500
        interval: 1m
    
    # 웹훅 (추가 검증)
    webhooks:
      - name: load-test
        url: http://flagger-loadtester/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://myapp-canary/"
  
  # 롤백 시 재시도 안 함
  skipAnalysis: false

# 배포 프로세스:
# 1. myapp-primary (100% 트래픽)
# 2. 새 버전 배포 → myapp-canary 생성
# 3. Canary: 5% 트래픽
# 4. 메트릭 체크 (1분)
# 5. 성공 → 10% 트래픽
# 6. 반복 → 50% 도달
# 7. 검증 완료 → primary 업데이트
# 8. Canary 제거

 

7.3 Blue-Green 배포

yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: myapp
  namespace: default
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  
  service:
    port: 80
  
  analysis:
    interval: 1m
    threshold: 10
    iterations: 10
    
    # Blue-Green: 한 번에 전환
    maxWeight: 0
    stepWeight: 100
    
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m

# 배포 프로세스:
# 1. myapp-primary (Blue, 100% 트래픽)
# 2. 새 버전 배포 → myapp-canary (Green)
# 3. Green 검증 (메트릭)
# 4. 검증 성공 → 즉시 100% 전환
# 5. Blue 제거

 

7.4 A/B Testing

yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: myapp
  namespace: default
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  
  service:
    port: 80
  
  analysis:
    interval: 1m
    threshold: 10
    maxWeight: 50
    stepWeight: 10
    
    # HTTP 헤더 기반 라우팅
    match:
      - headers:
          user-agent:
            regex: ".*Chrome.*"
      - headers:
          cookie:
            regex: "^(.*?;)?(test=true)(;.*)?$"
    
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m

# Chrome 사용자 + test=true 쿠키 → Canary
# 나머지 → Primary

 

8. 알림과 모니터링

8.1 Slack 알림

yaml
# clusters/production/notifications/slack-provider.yaml

apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: slack
  namespace: flux-system
spec:
  type: slack
  channel: deployments
  secretRef:
    name: slack-webhook

---
apiVersion: v1
kind: Secret
metadata:
  name: slack-webhook
  namespace: flux-system
type: Opaque
stringData:
  address: https://hooks.slack.com/services/xxx/yyy/zzz

---
# clusters/production/notifications/slack-alert.yaml

apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: on-call
  namespace: flux-system
spec:
  providerRef:
    name: slack
  
  eventSeverity: info
  
  eventSources:
    - kind: GitRepository
      name: '*'
    - kind: Kustomization
      name: '*'
    - kind: HelmRelease
      name: '*'
  
  summary: "Cluster: production"

 

8.2 Discord 알림

yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: discord
  namespace: flux-system
spec:
  type: discord
  channel: "1234567890"
  username: FluxBot
  secretRef:
    name: discord-webhook

---
apiVersion: v1
kind: Secret
metadata:
  name: discord-webhook
  namespace: flux-system
type: Opaque
stringData:
  address: https://discord.com/api/webhooks/xxx/yyy

 

8.3 Webhook 알림

yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: webhook
  namespace: flux-system
spec:
  type: generic
  address: https://example.com/webhook
  secretRef:
    name: webhook-token

---
apiVersion: v1
kind: Secret
metadata:
  name: webhook-token
  namespace: flux-system
type: Opaque
stringData:
  token: my-secret-token

---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: webhook-alert
  namespace: flux-system
spec:
  providerRef:
    name: webhook
  eventSources:
    - kind: Kustomization
      name: '*'

 

8.4 Prometheus 메트릭

bash
# Flux 컨트롤러는 Prometheus 메트릭 노출
# :8080/metrics

# 주요 메트릭:
# - gotk_reconcile_condition: Reconcile 상태
# - gotk_reconcile_duration_seconds: Reconcile 시간
# - controller_runtime_reconcile_total: Reconcile 횟수

# Prometheus 스크랩 설정
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceMonitor
metadata:
  name: flux-system
  namespace: flux-system
spec:
  selector:
    matchLabels:
      app.kubernetes.io/part-of: flux
  endpoints:
    - port: http-prom
      interval: 30s
EOF

# Grafana 대시보드
# https://grafana.com/grafana/dashboards/16714
# Flux Cluster Stats
# Flux Control Plane

 

9. 보안

9.1 Sealed Secrets

bash
# Sealed Secrets 설치
flux create source helm sealed-secrets \
  --url=https://bitnami-labs.github.io/sealed-secrets \
  --namespace=flux-system

flux create helmrelease sealed-secrets \
  --source=HelmRepository/sealed-secrets \
  --chart=sealed-secrets \
  --namespace=kube-system

# kubeseal CLI 설치
brew install kubeseal

# Secret 생성
kubectl create secret generic mysecret \
  --from-literal=password=mypassword \
  --dry-run=client \
  -o yaml > mysecret.yaml

# Seal (암호화)
kubeseal --format yaml < mysecret.yaml > mysealedsecret.yaml

# Git에 커밋 (안전)
git add mysealedsecret.yaml
git commit -m "Add sealed secret"
git push

# Flux가 자동으로 복호화하여 적용

 

9.2 SOPS (Mozilla)

bash
# SOPS 설치
brew install sops

# Age 키 생성
age-keygen -o age.txt

# Public 키 확인
cat age.txt | grep public

# .sops.yaml 생성
cat <<EOF > .sops.yaml
creation_rules:
  - path_regex: .*.yaml
    encrypted_regex: ^(data|stringData)$
    age: age1xxx...
EOF

# Secret 암호화
sops --encrypt --in-place secret.yaml

# Git에 커밋
git add secret.yaml .sops.yaml
git commit -m "Add encrypted secret"
git push

# Flux에서 SOPS 활성화
flux create kustomization apps \
  --source=GitRepository/app-repo \
  --path=./apps \
  --prune=true \
  --interval=5m \
  --decryption-provider=sops \
  --decryption-secret=sops-age

# Age 키를 Secret으로
cat age.txt | kubectl create secret generic sops-age \
  --namespace=flux-system \
  --from-file=age.agekey=/dev/stdin

 

9.3 Policy (OPA Gatekeeper)

yaml
# Gatekeeper 설치
flux create source helm gatekeeper \
  --url=https://open-policy-agent.github.io/gatekeeper/charts \
  --namespace=flux-system

flux create helmrelease gatekeeper \
  --source=HelmRepository/gatekeeper \
  --chart=gatekeeper \
  --namespace=gatekeeper-system

---
# Policy: 모든 컨테이너에 리소스 제한 필수

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredresources
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredResources
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredresources
        
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits.cpu
          msg := sprintf("Container %v has no CPU limit", [container.name])
        }

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredResources
metadata:
  name: must-have-resources
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]

 

10. 트러블슈팅

10.1 일반적인 문제

bash
# 문제 1: Reconcile 실패
flux get all --all-namespaces

# 로그 확인
flux logs --kind=Kustomization --name=apps

# 강제 Reconcile
flux reconcile kustomization apps --with-source

# 문제 2: Git 인증 실패
# Secret 확인
kubectl get secret -n flux-system

# SSH 키 재생성
flux create secret git app-repo \
  --url=ssh://git@github.com/user/repo \
  --ssh-key-algorithm=rsa \
  --ssh-rsa-bits=4096

# 문제 3: Image pull 실패
# Docker registry credentials 생성
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --namespace=flux-system

# ImageRepository에 추가
# spec.secretRef.name: regcred

# 문제 4: Helm release 실패
flux get helmreleases --all-namespaces

# 상세 로그
kubectl describe helmrelease myapp -n default

# 재시도
flux reconcile helmrelease myapp

# 문제 5: 느린 Reconcile
# Interval 확인
flux get sources git

# Interval 단축
flux create source git app-repo \
  --url=https://github.com/user/repo \
  --branch=main \
  --interval=30s

 

10.2 디버깅

bash
# Flux 시스템 체크
flux check

# 모든 리소스 상태
flux get all --all-namespaces

# 이벤트 확인
kubectl get events -n flux-system --sort-by='.lastTimestamp'

# Reconcile 추적
flux logs --follow --all-namespaces

# Suspend/Resume
flux suspend kustomization apps
flux resume kustomization apps

# 수동 Reconcile
flux reconcile source git app-repo
flux reconcile kustomization apps

# Reconcile 조건 확인
kubectl get kustomization apps -n flux-system -o yaml

# status.conditions 확인:
# - type: Ready
#   status: "True"
#   reason: ReconciliationSucceeded

 

10.3 성능 최적화

yaml
# Reconcile interval 조정
spec:
  interval: 5m  # 기본값: 1m

# Prune 비활성화 (대규모 클러스터)
spec:
  prune: false

# Health check 타임아웃
spec:
  timeout: 5m
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: app
      namespace: default

# 재시도 간격
spec:
  retryInterval: 2m

# Force 옵션
spec:
  force: true  # 리소스 재생성

 

11. 실무 체크리스트

Flux 사용 시:

초기 설정

  • Flux CLI 설치
  • GitHub Token 생성
  • Flux 부트스트랩
  • GitRepository 생성

애플리케이션 배포

  • 매니페스트 Git에 커밋
  • Kustomization 생성
  • Reconcile 확인
  • 알림 설정

Helm 통합

  • HelmRepository 추가
  • HelmRelease 생성
  • Values 관리
  • 업그레이드 전략

이미지 자동화

  • ImageRepository 생성
  • ImagePolicy 정의
  • ImageUpdateAutomation 설정
  • 마커 추가

멀티 클러스터

  • Base/Overlay 구조
  • 클러스터별 Kustomization
  • 환경별 구성
  • RBAC 설정

보안

  • Sealed Secrets 또는 SOPS
  • Private 저장소 인증
  • RBAC 최소 권한
  • Policy 적용

모니터링

  • Slack/Discord 알림
  • Prometheus 메트릭
  • Grafana 대시보드
  • 로그 수집

 

12. 결론

Flux는 GitOps의 표준입니다.

핵심 교훈:

  1. GitOps 네이티브 - Git이 진실의 원천
  2. Kubernetes 네이티브 - CRD 기반
  3. Pull 모델 - 클러스터가 Git에서 Pull
  4. 자동 조정 - Drift 자동 수정
  5. 멀티 테넌시 - 팀별 격리
  6. 프로그레시브 배포 - Canary, Blue-Green
  7. 이미지 자동화 - CI → Flux 자동 배포
  8. CNCF 프로젝트 - 커뮤니티 지원

Flux는 Kubernetes에 최적화된 GitOps 도구입니다. "Git에 푸시하면 자동 배포"라는 GitOps의 약속을 완벽하게 구현합니다. ArgoCD보다 단순하고, CI/CD보다 Kubernetes 네이티브합니다. GitOps로 클러스터를 선언적으로 관리하세요!

"Git Push = Auto Deploy. Flux!"

반응형

댓글