[Study][GitOps] ArgoCD 운영 구성 - 1 — 나무늘보의 IT생활
[Study][GitOps] ArgoCD 운영 구성 - 1
이번 스터디에서는 예전 스터디에서 테스트해봤던 ArgoCD 기본 동작외에 추가로 실제 운영에서 사용할만한 ArgoCD의 구성 및 기능에 대해서 테스트해보았다.ArgoCD 실습환경이전과 동일하게 Windows 11
ersia.tistory.com
해당 글에 이어서 다른 운영 기능을 테스트해본다.
ArgoCD에서의 멀티클러스터
하나의 ArgoCD에서 2개 이상의 클러스터를 등록해 사용할 수도 있다.
주의할 점으로 argocd.argoproj.io/secret-type: cluster 이 라벨이 있어야 Argo CD가 해당 Secret을 “클러스터 관리용”으로 인식한다.
두 번째 클러스터 생성
# 현재 클러스터 확인
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kind-ersia kind-ersia kind-ersia
# 두 번째 kind 클러스터 생성
kind create cluster --name ersia2 --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
EOF
# 클러스터 두 개가 모두 존재하는지 확인
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kind get clusters
ersia
ersia2
만약 두 번째 클러스터 생성 시 에러가 난다면 아래의 절차를 거쳐서 로그를 확인한다.
kind create cluster --name kind-ersia2 \
--image kindest/node:v1.32.8 \
--retain --wait 120s --verbosity 9 \ # 상세 로그 출력
--config - <<'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
EOF
# 컨테이너 로그 확인
# 보통 아래의 Too many open files 에러일 확률이 높다
docker logs kind-ersia2-control-plane --tail=200
...
Detected architecture x86-64.
Welcome to Debian GNU/Linux 12 (bookworm)!
Failed to create control group inotify object: Too many open files
Failed to allocate manager object: Too many open files
[!!!!!!] Failed to allocate manager object.
Exiting PID 1...
INFO: ensuring we can execute mount/umount even with userns-remap
...
# 아래의 명령어로 임시 조치
sudo sysctl fs.inotify.max_user_watches=524288
sudo sysctl fs.inotify.max_user_instances=512
https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files
새 클러스터에 접근하기 위한 사전작업
ArgoCD에서 새로 생성한 클러스터에 접근할 수 있도록 ServiceAccount 및 권한 생성 관련 정보를 추출한다.
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config use-context kind-ersia2
Switched to context "kind-ersia2".
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
kind-ersia kind-ersia kind-ersia
* kind-ersia2 kind-ersia2 kind-ersia2
# ServiceAccount 생성
kubectl create serviceaccount argocd-manager -n kube-system
# ClusterRole 및 바인딩
# 권한을 좀 세분화하게 테스트해봤으나, 잘되지 않아 full 권한을 일단 줘서 테스트함
kubectl apply -f - <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argocd-manager-all
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
- nonResourceURLs: ["*"]
verbs: ["*"]
EOF
kubectl apply -f - <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: argocd-manager-all-binding
subjects:
- kind: ServiceAccount
name: argocd-manager
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: argocd-manager-all
EOF
# 장기 토큰 Secret 명시적 생성
kubectl -n kube-system apply -f - <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: argocd-manager-long-lived-token
annotations:
kubernetes.io/service-account.name: argocd-manager
type: kubernetes.io/service-account-token
EOF
# 토큰 추출
sleep 2
TOKEN=$(kubectl -n kube-system get secret argocd-manager-long-lived-token \
-o jsonpath='{.data.token}' | base64 -d)
echo "${TOKEN:0:20}..."
# ArgoCD가 접근할 수 있는 Cluster Port 확인
PORT=$(kubectl config view --context kind-ersia2 --minify \
-o jsonpath='{.clusters[0].cluster.server}' \
| sed -E 's#https://127\.0\.0\.1:([0-9]+).*#\1#')
echo "Detected ersia2 API Port: $PORT"
추가로 kind 구성이기 때문에 새 클러스터에 접근하기 위해 새 클러스터가 사용하는 kind 네트워크 IP를 확인한다.
NEW_CLUSTER_IP=$(docker inspect -f '{{ .NetworkSettings.Networks.kind.IPAddress }}' ersia2-control-plane)
echo "NEW_CLUSTER_IP: $NEW_CLUSTER_IP"
NEW_CLUSTER_IP: 172.18.0.6
컨트롤 플레인에서 API용 포트는 6443이므로 추출한 IP와 6443 Port로 통신을 시도해야 한다.
참고 : Ports and Protocols | Kubernetes
ArgoCD가 구성된 클러스터에 추출한 두 번째 클러스터에 대한 Secret 생성
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config use-context kind-ersia
Switched to context "kind-ersia".
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kind-ersia kind-ersia kind-ersia
kind-ersia2 kind-ersia2 kind-ersia2
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: kind-ersia2-cluster
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
stringData:
name: kind-ersia2
server: https://172.18.0.6:6443
config: |
{
"bearerToken": "${TOKEN}",
"tlsClientConfig": {
"insecure": true
}
}
type: Opaque
EOF
생성 후 아래와 같이 등록된 클러스터를 확인할 수 있다.
아직 새로 생성한 클러스터에는 아무런 앱이 없어서 상태가 unknown으로 표시된다.
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# argocd --grpc-web cluster list
SERVER NAME VERSION STATUS MESSAGE PROJECT
https://172.18.0.6:6443 kind-ersia2
https://kubernetes.default.svc in-cluster 1.32 Successful
ArgoCD의 Project
Argo CD에서 Project(AppProject) 는 여러 애플리케이션을 논리적으로 묶고 접근 권한을 제어하는 단위다.
- 어떤 Git 저장소에서 코드를 가져올 수 있는지 제한할 수 있는 기능
- 배포 가능한 클러스터와 네임스페이스를 지정하는 기능
- 자동 동기화나 승인 프로세스 정책 지정
이런 Project의 기능을 사용하면 개발앱이 운영환경에 잘못 배포되는 케이스 등 미리 방지할 수 있다.
Project 생성
cat <<'EOF' > project-multicluster.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: multi-demo
namespace: argocd
spec:
description: "Project to control multi-cluster deployment"
destinations:
# 기본(Argo CD가 설치된 클러스터)
- namespace: demo
server: https://kubernetes.default.svc
# 원격 클러스터 (kind-ersia2)
- namespace: demo
server: https://172.18.0.6:6443
# 허용된 Git 저장소, 여기선 이전에 테스트한 nginx manifest 배포용 저장소를 사용
sourceRepos:
- https://github.com/<GITHUB_USER>/<REPO>.git
EOF
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl apply -f project-multicluster.yaml
appproject.argoproj.io/multi-demo created

새로 생성한 클러스터에 Git 저장소의 nginx manifest를 배포하도록 설정
# 이전과 마찬가지로 각자 환경에 맞는 Github URL과 Port로 치환
cat <<'EOF' > app-nginx-remote.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-remote
namespace: argocd
spec:
project: multi-demo
source:
repoURL: https://github.com/<GITHUB_USER>/<REPO>.git
targetRevision: main
path: manifests/nginx
destination:
server: https://172.18.0.6:6443
namespace: demo
syncPolicy:
automated:
prune: true
selfHeal: true
EOF
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl apply -f app-nginx-remote.yaml
application.argoproj.io/nginx-remote created
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# argocd --grpc-web cluster list
SERVER NAME VERSION STATUS MESSAGE PROJECT
https://172.18.0.6:6443 kind-ersia2 1.32 Successful
https://kubernetes.default.svc in-cluster 1.32 Successful
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config use-context kind-ersia2
Switched to context "kind-ersia2".
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kubectl -n demo get all
NAME READY STATUS RESTARTS AGE
pod/nginx-77d77d8b8d-pmdc5 1/1 Running 0 11m
pod/nginx-77d77d8b8d-rwpzn 1/1 Running 0 11m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx ClusterIP 10.96.146.115 <none> 80/TCP 11m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 2/2 2 2 12m
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-77d77d8b8d 2 2 2 12m
정상적으로 앱이 배포된 것을 확인할 수 있다.

Project 정책 테스트
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config use-context kind-ersia
Switched to context "kind-ersia".
# 허용되지 않은 네임스페이스로 변경 시도 → 차단 확인
kubectl -n argocd patch application nginx-remote --type merge -p \
'{"spec":{"destination":{"namespace":"forbidden"}}}'
# 허용되지 않은 리포로 변경 시도 → 차단 확인
kubectl -n argocd patch application nginx-remote --type merge -p \
'{"spec":{"source":{"repoURL":"https://github.com/other/repo.git"}}}'
# 차단되어서 동기화가 되지않은 것을 확인
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl -n argocd get application nginx-remote -o jsonpath='{.status.conditions[*].message}{"\n"}'
application destination server 'https://172.18.0.6:6443' and namespace 'forbidden' do not match any of the allowed destinations in project 'multi-demo' application repo https://github.com/other/repo.git is not permitted in project 'multi-demo'
# 대상 클러스터에는 리소스가 그대로 유지됨
(⎈|kind-ersia:N/A) root@DESKTOP-O4EPQ9T:~# kubectl config use-context kind-ersia2
Switched to context "kind-ersia2".
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~#
(⎈|kind-ersia2:N/A) root@DESKTOP-O4EPQ9T:~# kubectl -n demo get deploy,svc,pod
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 2/2 2 2 28m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx ClusterIP 10.96.146.115 <none> 80/TCP 28m
NAME READY STATUS RESTARTS AGE
pod/nginx-77d77d8b8d-pmdc5 1/1 Running 0 28m
pod/nginx-77d77d8b8d-rwpzn 1/1 Running 0 28m
ArgoCD UI에서도 아래와 같이 동기화 에러를 확인할 수 있다.


정상 복구 후 앱 동기화 확인
# 허용 네임스페이스로 되돌리기
kubectl -n argocd patch application nginx-remote --type merge -p \
'{"spec":{"destination":{"namespace":"demo"}}}'
# 허용 Repo로 되돌리기
kubectl -n argocd patch application nginx-remote --type merge -p \
'{"spec":{"source":{"repoURL":"https://github.com/<GITHUB_USER>/<REPO>.git"}}}'
