콘텐츠로 이동
Study Note온프렘 쿠버네티스

9. 배포 — Argo CD와 GitOps

“이 클러스터에 뭐가 깔려 있죠?” 에 Git 저장소 주소로 답할 수 있어야 한다

이 장에서 처음 나오는 말6개
GitOps
클러스터의 원하는 상태를 Git에 두고, 컨트롤러가 그 상태를 클러스터에 계속 맞추는 방식. 배포가 kubectl apply가 아니라 머지가 된다.
드리프트Drift
Git에 적힌 상태와 클러스터 실제 상태가 어긋난 것. 누군가 kubectl edit으로 급하게 고친 흔적이 대부분이다.
App of Apps
Argo CD Application 하나가 다른 Application들을 배포하는 구조. 클러스터 전체를 한 리소스에서 펼친다.
sync wave동기화 웨이브
리소스에 순번을 매겨 설치 순서를 강제하는 Argo CD 기능. CRD가 먼저, 그것을 쓰는 리소스가 나중이어야 할 때 쓴다.
부트스트랩Bootstrap
아무것도 없는 클러스터에서 자기 자신을 관리할 첫 조각을 손으로 심는 일. Argo CD 최초 설치가 그것이다.
미러 레지스트리Mirror Registry
외부 이미지를 사내에 복제해 두는 저장소. 인터넷에 직접 못 나가는 온프렘에서는 사실상 필수다.

문제 — 손으로 깐 것은 재현되지 않는다

섹션 제목: “문제 — 손으로 깐 것은 재현되지 않는다”

여기까지 오면 클러스터에 이만큼이 깔려 있다.

MetalLB · Gateway 구현체 · cert-manager · external-dns · oauth2-proxy · CloudNativePG · kube-prometheus-stack · Loki · Tempo · Grafana · Alloy · Velero — 열두 개, 각각 Helm values가 수십 줄.

질문손으로 깔았다면
“지금 Loki 보존 기간이 며칠이죠?”클러스터에 들어가 봐야 안다
“누가 이 값을 바꿨죠?”알 수 없다
“이 설정 왜 이렇게 돼 있죠?”3개월 전 그 사람만 안다
“DR(Disaster Recovery, 재해 복구) 사이트에 같은 걸 세워 주세요”처음부터 다시, 기억에 의존해서
“테스트 클러스터랑 뭐가 다르죠?”비교할 방법이 없다

Git에 두면 이 다섯이 전부 해결된다. 그게 GitOps의 실용적인 이유다 — “선언적”이라는 말보다 이쪽이 와닿는다.

Argo CD — 원하는 상태를 계속 맞춘다

섹션 제목: “Argo CD — 원하는 상태를 계속 맞춘다”
사람이 PR과 머지로 Git 저장소를 고치면 Argo CD가 주기적으로 읽어 차이를 클러스터에 적용하고, 실제 상태 보고와 드리프트 알림이 되돌아오는 GitOps 순환
Argo CDFlux
UI있다 (실물 화면이 운영에서 꽤 유용하다)없다 (CLI·CRD)
모델Application 중심여러 컨트롤러(Source·Kustomize·Helm)
고를 때사람이 화면으로 상태를 보는 게 중요하면완전 자동화·GitOps 순수주의

이 덱은 Argo CD를 기준으로 쓴다 — 온프렘 운영에서 “지금 뭐가 안 맞나”를 화면으로 보여 주는 것의 가치가 크기 때문이다.

  • 디렉터리platform/
    • 디렉터리bootstrap/
      • root-app.yaml 모든 것의 시작 — 이것 하나만 손으로 적용한다
    • 디렉터리infra/
      • 디렉터리metallb/ values.yaml
        • …
      • 디렉터리gateway/ values.yaml
        • …
      • 디렉터리cert-manager/ values.yaml, clusterissuer.yaml
        • …
      • 디렉터리external-dns/ values.yaml
        • …
    • 디렉터리data/
      • 디렉터리cnpg-operator/
        • …
      • 디렉터리cnpg-clusters/ platform-pg.yaml
        • …
    • 디렉터리observability/
      • 디렉터리kube-prometheus-stack/ values.yaml, rules/
        • …
      • 디렉터리loki/ values.yaml
        • …
      • 디렉터리tempo/ values.yaml
        • …
      • 디렉터리grafana/ values.yaml, dashboards/
        • …
    • 디렉터리apps/
      • 디렉터리team-a/
        • …
      • 디렉터리team-b/
        • …
# root-app.yaml — App of Apps
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: platform-root
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.internal/platform/platform.git
targetRevision: main
path: bootstrap/apps # 여기에 각 컴포넌트의 Application이 들어 있다
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true # Git에서 지운 것은 클러스터에서도 지운다
selfHeal: true # 손으로 고친 것을 되돌린다

플랫폼은 순서가 있다. CRD가 없으면 그 CRD를 쓰는 리소스가 실패하고, Issuer가 없으면 Certificate가 발급되지 않는다.

metadata:
annotations:
argocd.argoproj.io/sync-wave: "-2" # 숫자가 작을수록 먼저
wave내용왜 이 순서
-3네임스페이스, CRD나머지가 이걸 참조한다
-2MetalLB, cert-manager, CNPG 오퍼레이터컨트롤러가 먼저 떠야 CR을 처리한다
-1ClusterIssuer, IPAddressPool, Gateway오퍼레이터가 살아 있어야 만들어진다
0CNPG Cluster, Keycloak, 오브젝트 스토리지 연동상태 계층
1관측 스택, Argo CD 자신의 설정
2앱

시크릿 — 여기서는 자리만 잡는다

섹션 제목: “시크릿 — 여기서는 자리만 잡는다”

지금까지 나온 것만 해도 비밀이 이만큼이다 — 오브젝트 스토리지 액세스 키, Postgres 비밀번호, Keycloak client secret, DNS API 토큰, Grafana OAuth secret. 이걸 평문으로 Git에 올릴 수는 없는데, GitOps는 Git이 단일 소스여야 성립한다.

방식Git에 올라가는 것누가 푸나
Sealed Secrets암호문클러스터 안 컨트롤러
ESO + 외부 금고참조만오퍼레이터가 금고에서 읽어 온다
SOPS(Secrets OPerationS) + age암호문(파일)배포 시 사람·CI(Continuous Integration, 지속적 통합) 파이프라인

온프렘에서 배포가 실패하는 흔한 이유는 매니페스트가 아니라 이미지를 못 받아 오는 것이다.

문제대응
인터넷 직접 접근 불가사내 레지스트리(Harbor 등)에 미러링
외부 레지스트리 rate limit마찬가지. 미러가 rate limit도 해결한다
에어갭이미지를 tar로 반입해 push. 차트도 함께
태그가 조용히 바뀜다이제스트 고정(@sha256:…) 또는 최소한 고정 태그. latest 금지
사내 CA를 노드가 안 믿음노드의 CA 저장소에 추가 (4장)
# Helm values에서 레지스트리를 한 곳으로 몰아 두면 미러 전환이 한 줄이 된다
global:
imageRegistry: registry.example.internal
imagePullSecrets: [ regcred ]
항목내용
prune이 무섭다처음엔 prune: false로 두고 화면에서 차이만 본다. 익숙해지면 켠다
CRD가 지워질 뻔한다CRD에 argocd.argoproj.io/sync-options: Prune=false를 달아 보호한다. CRD가 지워지면 그 CR의 데이터가 전부 사라진다
드리프트가 계속 난다selfHeal이 켜져 있는데 누군가 계속 손으로 고친다는 뜻이다. 왜 손으로 고쳐야 하는지를 먼저 없앤다
Helm 차트 버전 고정targetRevision을 범위로 두면 어느 날 조용히 올라간다. 고정한다
차트 저장소 이전Loki 차트처럼 저장소가 옮겨 가는 일이 있다 (관측 덱 3장)
Argo CD 자신의 알림argocd_app_info{sync_status="OutOfSync"}를 Prometheus 알림으로 (관측 덱 2장)
터미널 창
# ① 전체 앱 상태 — OutOfSync·Degraded가 있나
kubectl -n argocd get applications
argocd app list
# ② 왜 다른가
argocd app diff platform-observability
# ③ 시크릿이 실제로 만들어졌나
kubectl -n observability get secret loki-s3 -o jsonpath='{.data}' | jq 'keys'
# ④ 이미지가 사내 레지스트리에서 오나
kubectl -n observability get pods -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | sort -u
# ⑤ sync wave 순서대로 떴나 (앞 wave가 Degraded면 뒤는 볼 필요 없다)
kubectl -n argocd get applications -o custom-columns=\
'NAME:.metadata.name,SYNC:.status.sync.status,HEALTH:.status.health.status'
  • GitOps의 실용적 이유는 “뭐가 깔려 있고, 누가 언제 바꿨고, 어떻게 다시 세우나” 에 답하는 것이다
  • App of Apps + sync wave로 클러스터 전체와 설치 순서를 선언한다
  • 닭과 달걀을 인정한다 — Argo CD 최초 설치, Git 저장소, 레지스트리는 클러스터 밖에 둔다
  • 시크릿은 부트스트랩 순서에서 앱보다 먼저 온다 — 실무 상세는 10장
  • 온프렘 배포 실패의 단골은 매니페스트가 아니라 이미지다 — 사내 레지스트리와 태그 고정
  • CRD는 prune에서 보호한다. CRD가 지워지면 그 CR의 데이터가 전부 사라진다