지금까지 실습 클러스터에서 kubectl apply, kubectl scale,
kubectl patch 를 손으로 쳐 왔다. 문제가 하나 있다. 무엇을 왜 바꿨는지가
아무 데도 안 남는다.
ArgoCD 는 그걸 git 으로 옮긴다. YAML 을 저장소에 두면 ArgoCD 가 클러스터를
거기에 맞춰 준다. 이 글은 그걸 올리고, 실제로 막혔던 두 지점을 정리한다.
무엇을 하는가#
| ArgoCD | v3.5.2, 파드 7개 |
| git 저장소 | 공개 저장소 하나 (인증 설정이 필요 없어진다) |
| UI 주소 | http://localhost/argocd/ |
| 관리 대상 | 저장소의 manifests/stateful-demo 경로 |
GitOps 가 무엇인가#
한 줄로 줄이면 이렇다.
git 에 적힌 것이 정답이고, 클러스터를 거기에 맞춘다.
지금까지 하던 방식과 방향이 반대다.
| 지금까지 | GitOps | |
|---|---|---|
| 바꾸는 곳 | 클러스터에 직접 kubectl |
git 의 YAML |
| 적용 | 사람이 명령을 친다 | ArgoCD 가 맞춘다 |
| 기록 | 안 남는다 | 커밋으로 남는다 |
| 되돌리기 | 기억해서 반대로 친다 | git revert |
참고
"동기화(sync)" 와 "드리프트(drift)" 라는 말이 계속 나온다.
클러스터가 git 과 같은 상태면Synced, 다르면OutOfSync다.
누가 손으로 클러스터를 바꿔서 git 과 어긋난 상태를 드리프트라고 한다.
준비물#
- 쿠버네티스 클러스터. 없으면 앞 글대로 만든다
- git 저장소 하나. 이 글은 공개 저장소를 쓴다. 비공개로 해도 되지만 ArgoCD 에 접근 키를 등록하는 단계가 늘어난다
- 걸리는 시간 30분 안팎
주의
저장소를 공개로 만들 거라면 올리기 전에 내용을 한 번 훑는다. 매니페스트에
집 네트워크 주소나 내부 호스트 이름이 주석으로 들어가 있기 쉽다. 나는 맥의
LAN 주소가 주석에 있어서 마지막 자리를 가리고 올렸다.
1. ArgoCD 를 설치한다#
공식 매니페스트 하나면 된다.
kubectl create namespace argocd
kubectl apply -n argocd --server-side -f
https://raw.githubusercontent.com/argoproj/argo-cd/v3.5.2/manifests/install.yaml
참고
--server-side를 붙인 이유가 있다. 보통kubectl apply는 적용한 내용 전체를
어노테이션에 적어 두는데, 이 파일은 1.8MB 라 크기 제한(약 262KB)에 걸려
실패한다.--server-side는 그 방식을 쓰지 않아 통과한다.
파드가 일곱 개 뜬다. 전부 준비될 때까지 기다린다.
kubectl wait -n argocd --for=condition=ready pod --all --timeout=600s
argocd-application-controller-0 1/1 Running
argocd-applicationset-controller-... 1/1 Running
argocd-dex-server-... 1/1 Running
argocd-notifications-controller-... 1/1 Running
argocd-redis-... 1/1 Running
argocd-repo-server-... 1/1 Running
argocd-server-... 1/1 Running
팁
설치 파일에는 NetworkPolicy 가 7개 들어 있다. 네트워크 플러그인이 그걸
지원하지 않으면 조용히 무시된다. Calico 로 바꿔 둔 이야기가
여기서 값을 한다.
2. 매니페스트를 git 에 올린다#
ArgoCD 는 git 저장소를 본다. 로컬 폴더는 못 본다. 클러스터 안에서 도는
프로그램이라 내 맥의 파일 시스템에 닿을 수 없기 때문이다.
cd ~/k8s
git init -b main
git add -A
git commit -m "kind 실습 클러스터 매니페스트"
git remote add origin git@github.com:<사용자>/<저장소>.git
git push -u origin main
3. Application 을 만든다#
Application 은 "이 저장소의 이 경로를, 이 클러스터의 이 네임스페이스에
맞춰 줘" 라고 적은 것이다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: stateful-demo
namespace: argocd # Application 자체는 argocd 네임스페이스에 둔다
spec:
project: default
source: # 어디를 볼지
repoURL: https://github.com/<사용자>/<저장소>.git
targetRevision: main # 어느 브랜치
path: manifests/stateful-demo # 저장소 안 어느 경로
destination: # 어디에 넣을지
server: https://kubernetes.default.svc # 자기 자신(이 클러스터)
namespace: stateful-demo
syncPolicy:
syncOptions:
- CreateNamespace=true # 네임스페이스가 없으면 만들어 준다
참고
여기서는 자동 동기화를 일부러 켜지 않았다. 먼저 손으로 눌러 보고 동작을
이해한 뒤 6단계에서 켠다. 처음부터 켜면 무슨 일이 일어나는지 보기 어렵다.
만들고 상태를 본다.
kubectl get application -n argocd stateful-demo
-o custom-columns='SYNC:.status.sync.status,HEALTH:.status.health.status'
SYNC HEALTH
OutOfSync Missing
OutOfSync / Missing — git 에는 있는데 클러스터에는 없다는 뜻이다.
아직 아무것도 안 만들었으니 맞는 상태다. 무엇을 봤는지도 알려 준다.
PersistentVolumeClaim lonely OutOfSync
Pod lonely-consumer OutOfSync
Service notes OutOfSync
Deployment notes-deploy OutOfSync
StatefulSet notes OutOfSync
4. 첫 동기화#
UI 나 argocd CLI 로 누르는 게 보통이지만, kubectl 로도 된다.
kubectl patch application stateful-demo -n argocd --type merge -p '{
"operation": {
"initiatedBy": {"username": "admin"},
"sync": {"revision": "main", "syncOptions": ["CreateNamespace=true"]}
}
}'
+0초 OutOfSync/Missing/Running
+1초 OutOfSync/Progressing/Running
+1초 Synced/Progressing/Succeeded
1초 만에 끝났다. 클러스터를 보면 git 에 적힌 것들이 만들어져 있다.
pod/lonely-consumer 1/1
pod/notes-0 0/1
pod/notes-deploy-... 1/1
persistentvolumeclaim/data-notes-0 Bound
persistentvolumeclaim/lonely Bound
5. UI 를 연다 — 맥에서는 경로로 열어야 한다#
여기서 한 번 막힌다. 보통 문서는 argocd.내도메인 처럼 호스트 이름으로
나누라고 한다. 그런데 로컬 실습에서는 두 가지가 걸린다.
첫째, 맥은 argocd.localhost 를 해석하지 않는다.
ping -c1 argocd.localhost
ping: cannot resolve argocd.localhost: Unknown host
리눅스는 *.localhost 를 자동으로 127.0.0.1 로 보내 주지만 macOS 는 아니다.
쓰려면 /etc/hosts 를 고쳐야 하는데 관리자 권한이 필요하다.
둘째, localhost/ 는 이미 다른 앱이 쓰고 있다. 앞 글에서
올린 echo 앱이 거기 붙어 있다.
그래서 경로로 나눈다. localhost/argocd 로 간다.
다만 경로로 나눌 때는 ArgoCD 에게도 알려 줘야 한다.
kubectl patch configmap argocd-cmd-params-cm -n argocd --type merge -p '{
"data": {
"server.rootpath": "/argocd",
"server.insecure": "true"
}
}'
kubectl rollout restart deployment argocd-server -n argocd
server.rootpath— "나는 /argocd 아래에 있다" 를 알려 준다. 이걸 안 하면 화면은 뜨는데 CSS·JS 를/기준으로 찾아서 빈 화면이나 깨진 화면이 나온다.server.insecure— ArgoCD 가 평문 HTTP 로 받게 한다. 앞단 nginx 가 이미 바깥쪽을 맡고 있으니 안쪽까지 TLS 를 쓸 이유가 없다.
주의
ConfigMap 을 고쳐도 돌고 있는 파드는 모른다. 반드시 재시작해야 반영된다.
설정을 바꿨는데 그대로라면 이걸 빠뜨린 경우가 많다.
Ingress 를 붙인다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server
namespace: argocd
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /argocd
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
확인한다.
curl -s -o /dev/null -w '%{http_code}n' http://localhost/argocd/
curl -s http://localhost/argocd/ | grep -o '<title>[^<]*</title>'
200
<title>Argo CD</title>
기존 앱도 그대로인지 함께 본다. 경로로 나눴으니 둘 다 살아 있어야 한다.
curl -s http://localhost/hostname
echo-557947968f-l4lpx
브라우저에서 http://localhost/argocd/ 를 열면 로그인 화면이 나온다.
화면이 깨지지 않고 제대로 그려지면 rootpath 가 맞게 들어간 것이다.
초기 비밀번호는 시크릿에 들어 있다. 사용자 이름은 admin 이다.
kubectl -n argocd get secret argocd-initial-admin-secret
-o jsonpath='{.data.password}' | base64 -d; echo
팁
로그인한 뒤 비밀번호를 바꾸고
argocd-initial-admin-secret은 지우는 것이
권장된다. 실습 클러스터라도 습관을 들여 두면 좋다.
6. 자동 동기화를 켜면 kubectl 이 안 통한다#
여기가 GitOps 를 처음 쓸 때 가장 놀라는 부분이다.
먼저 자동 동기화가 꺼진 상태에서 손으로 늘려 본다.
kubectl scale statefulset notes -n stateful-demo --replicas=5
replicas=5
ArgoCD 상태: OutOfSync
늘어나긴 했다. ArgoCD 는 "git 과 다르다" 고 알려만 주고 건드리지 않는다.
이제 자동 동기화를 켠다.
kubectl patch application stateful-demo -n argocd --type merge -p '{
"spec": {"syncPolicy": {
"automated": {"prune": true, "selfHeal": true},
"syncOptions": ["CreateNamespace=true"]
}}
}'
+0초 replicas=5 sync=OutOfSync
+0초 replicas=3 sync=OutOfSync ← 되돌려졌다
+0초 replicas=3 sync=Synced
1초도 안 걸려 3 으로 돌아갔다. git 에 replicas: 3 이라고 적혀 있기 때문이다.
selfHeal: true— 클러스터가 git 과 달라지면 되돌린다prune: true— git 에서 지운 리소스를 클러스터에서도 지운다
주의
이제부터
kubectl scale,kubectl edit은 일시적으로만 먹는다. 바꾸려면
git 을 고쳐야 한다. 이게 GitOps 의 핵심이지만, 모르고 쓰면 "왜 자꾸 원복되지"
하고 한참 헤맨다.HPA 로 파드 수를 자동 조절한다면 충돌한다. 그때는
ignoreDifferences로
ArgoCD 가replicas를 관리하지 않게 빼 준다.
7. immutable 필드를 커밋하면 무한 재시도에 빠진다#
앞 글에서 podManagementPolicy 는 만든 뒤에
못 바꾸는 필드라는 것을 봤다. 이걸 git 에 커밋하면 어떻게 될까.
spec:
serviceName: notes
replicas: 3
podManagementPolicy: Parallel # 추가
git commit -am "podManagementPolicy 추가"
git push
ArgoCD 는 당연히 실패한다.
error when patching "/dev/shm/1855032735": StatefulSet.apps "notes" is invalid:
spec.podManagementPolicy: Invalid value: "Parallel": field is immutable.
Retrying attempt #2 at 11:56AM.
문제는 마지막 줄이다. ArgoCD 는 계속 재시도한다. 스스로 풀리지 않는다.
여기서 한 번 더 막혔다#
문서를 보면 Replace=true, Force=true 라는 동기화 옵션이 있다.
apply 대신 지우고 다시 만들게 하는 것이다. 그래서 넣어 봤는데 안 먹었다.
확인해 보니 이렇다.
kubectl get application -n argocd stateful-demo
-o jsonpath='{.status.operationState.operation.sync.syncOptions}'
["CreateNamespace=true"]
새로 준 옵션이 빠져 있다. 이전 동기화 작업이 아직 재시도 중이라, ArgoCD 가
새 작업을 받지 않은 것이다. 진행 중인 작업이 있으면 새 요청은 무시된다.
푼 방법 — 리소스를 지운다#
재시도가 실패하는 이유는 기존 리소스를 patch 하려 해서다. 리소스가 없으면
patch 가 아니라 create 가 되므로 immutable 제약에 걸리지 않는다.
kubectl delete statefulset notes -n stateful-demo
+0초 OutOfSync/Running
+83초 Synced/Succeeded ← 재시도가 통과했다
kubectl get statefulset notes -n stateful-demo
-o jsonpath='{.spec.podManagementPolicy}'
Parallel
참고
데이터는 무사하다. StatefulSet 을 지워도 PVC 는 남는다. 기본 정책이
whenDeleted: Retain이기 때문이다. 새로 만들어진 파드가 예전 PVC 를 다시 문다.
notes-0: force replace 전에 쓴 글왜 그런지는 StatefulSet 과 PVC 글에 정리했다.
Parallel 로 바뀐 것도 눈으로 확인된다. 파드를 늘려 보면 번호 순서를 안 지키고
한꺼번에 뜬다.
+0초 notes-0(Running) notes-1(Running) notes-2(Running) notes-3(ContainerCreating) notes-4(ContainerCreating)
OrderedReady 였다면 3번이 Running 이 된 뒤에야 4번이 시작됐다.
팁
참고로 위 파드들은 곧 사라졌다. 6단계에서 켜 둔
selfHeal이 git 의
replicas: 3에 맞춰 되돌렸기 때문이다. 자동 동기화를 켜 둔 채로는
스케일 실험이 안 된다. 실험하려면 잠시 끄거나 git 을 고쳐야 한다.
정리#
- GitOps 는 방향이 반대다. 클러스터를 고치는 게 아니라 git 을 고치고, ArgoCD 가 클러스터를 맞춘다. 무엇을 왜 바꿨는지가 커밋으로 남는다.
--server-side없이는 설치가 실패한다. 매니페스트가 1.8MB 라 어노테이션 크기 제한에 걸린다.- 맥에서 UI 를 열려면 경로로 나눈다.
argocd.localhost는 해석되지 않는다. 경로로 나눌 때는server.rootpath를 꼭 같이 설정한다. 안 하면 화면이 깨진다. ConfigMap 을 고친 뒤 재시작해야 반영된다. selfHeal을 켜면kubectl수정은 되돌려진다. 이게 정상 동작이다. HPA 처럼 클러스터가 스스로 바꾸는 값이 있다면ignoreDifferences로 빼 준다.- immutable 필드를 커밋하면 무한 재시도에 빠진다. 게다가 그동안은 새 동기화 옵션도 안 먹는다. 리소스를 지워 create 로 만들면 풀린다. PVC 는 남으니 데이터는 잃지 않는다.

댓글