CI/CD 6주차(Argo CD)[3] - App of Apps, ApplicationSet

2025. 11. 22. 04:01CICD

실습 환경 구성

 

Kind(k8s) -  mgmt 배포

kind create cluster --name mgmt --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  labels:
    ingress-ready: true
  
  # 1. API 서버 포트 매핑 추가 (172.30.1.39 바인딩)
  extraPortMappings:
  - containerPort: 6443
    hostPort: 6444            # 호스트 PC에서 사용할 포트
    listenAddress: "172.30.1.39" # <--- 로컬 PC IP로 고정 바인딩(필자에 경우 해당 IP)
    protocol: TCP
    
  # 기존에 있던 포트 매핑들
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
  - containerPort: 30000
    hostPort: 30000
    protocol: TCP
    
  # 2. kubeadm 설정을 패치하여 인증서(SANs)에 IP 추가
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
      ExtraArgs:
        bind-address: 0.0.0.0 # 컨테이너 내부 API 서버는 모든 IP에 바인딩
      certSANs:
      - "172.30.1.39"     # <--- API 서버 인증서에 호스트 IP를 추가
      - "127.0.0.1"       # 기본 로컬호스트 접근 유지
      - "0.0.0.0"         # 기본 0.0.0.0 바인딩 유지
---
EOF

 

Kind(k8s) - NGINX-ingress 배포 및 SSL passthrough 설정

# NGINX Ingress 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

# SSL passthrough
kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
        - --enable-ssl-passthrough' | kubectl apply -f -

 

로컬 PC - OpenSSL 개인키, 인증키 생성(argocd.example.com)

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout argocd.example.com.key \
  -out argocd.example.com.crt \
  -subj "/CN=argocd.example.com/O=argocd"

 

Kind(k8s) - 시크릿 생성

# ArgoCD 네임스페이스 생성
kubectl create ns argocd

# TLS 시크릿 생성
kubectl -n argocd create secret tls argocd-server-tls \
  --cert=argocd.example.com.crt \
  --key=argocd.example.com.key

 

Helm - Values 생성

cat <<EOF > argocd-values.yaml
global:
  domain: argocd.example.com

server:
  ingress:
    enabled: true
    ingressClassName: nginx
    annotations:
      nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
      nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    tls: true
EOF

 

Helm - Kind(k8s)에 ArgoCD 배포

# Helm Repo 추가
helm repo add argo https://argoproj.github.io/argo-helm

# Helm ArgoCD 배포
helm install argocd argo/argo-cd --version 9.0.5 -f argocd-values.yaml --namespace argocd

 

로컬 PC - 설정(/etc/hosts 변경)

# /etc/hosts 도메인 변조
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts

 

로컬 PC - 접속 테스트(curl 통신 및 argoCLI 접속)

# curl 통신 확인
curl -vk https://argocd.example.com/

# Argo CD 암호 확인
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo

# 암호 변수 설정
ARGOPW={최초 암호}

# argo CLI 로그인
argocd login argocd.example.com --insecure --username admin --password $ARGOPW

# argo URL 접속(argocd.example.com)
open "http://argocd.example.com"

TLS 통신 및 200 OK 확인
argo CLI 접속 확인
argoCD URL 확인(리다이렉트 확인)

 

 


Kind(k8s) -  dev 및 prd 배포

로컬 PC - docker 사전 확인

docker network ls
docker network inspect kind | jq

 

Kind(k8s) - dev/prd 배포

# dev k8s 배포
kind create cluster --name dev --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  # API 서버 포트 (6443)와 31000 포트 매핑 설정
  extraPortMappings:
  - containerPort: 6443
    hostPort: 6445           # 6443 포트를 호스트 PC의 6445 포트에 매핑
    listenAddress: "172.30.1.39" # 호스트 IP에 바인딩
    protocol: TCP
  - containerPort: 31000
    hostPort: 31000
    protocol: TCP
  
  # kubeadm 설정을 패치하여 API 서버 인증서에 호스트 IP 추가
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
      ExtraArgs:
        bind-address: 0.0.0.0
      certSANs:
      - "172.30.1.39"     # 인증서에 호스트 IP를 추가
      - "127.0.0.1"
      - "0.0.0.0"
---
EOF

# prd k8s 배포
kind create cluster --name prd --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  
  # API 서버 포트 (6443)와 32000 포트 매핑 설정
  extraPortMappings:
  - containerPort: 6443
    hostPort: 6446           # 6443 포트를 호스트 PC의 6446 포트에 매핑
    listenAddress: "172.30.1.39" # 호스트 IP에 바인딩
    protocol: TCP
  - containerPort: 32000
    hostPort: 32000
    protocol: TCP
  
  # kubeadm 설정을 패치하여 API 서버 인증서에 호스트 IP 추가
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
      ExtraArgs:
        bind-address: 0.0.0.0
      certSANs:
      - "172.30.1.39"     # 인증서에 호스트 IP를 추가
      - "127.0.0.1"
      - "0.0.0.0"
---
EOF

 

Kind(k8s) - dev/prd/mgmt 확인

kubectl config get-contexts

 

Kind(k8s) - k8s 자격증명 변경(kind-prd > kind-mgmt)

# mgmt k8s 자격증명 변경
kubectl config use-context kind-mgmt
kubectl config get-contexts

 

로컬PC - alias 설정

alias k8s1='kubectl --context kind-mgmt'
alias k8s2='kubectl --context kind-dev'
alias k8s3='kubectl --context kind-prd'

 

로컬PC - 도커 네트워크 및 통신 확인

docker network inspect kind | grep -E 'Name|IPv4Address'

# 도커 컨테이너 - 컨테이너 내부 도메인 통신 확인
docker ps
docker exec -it mgmt-control-plane curl -sk https://dev-control-plane:6443/version
docker exec -it mgmt-control-plane curl -sk https://prd-control-plane:6443/version
docker exec -it dev-control-plane curl -sk https://prd-control-plane:6443/version

# local > 컨테이너 Control Plane 통신 확인
kubectl get node -v=6 --context kind-mgmt
kubectl get node -v=6 --context kind-dev
kubectl get node -v=6 --context kind-prd

통신 양호 확인

 

로컬 PC -  로컬에서 컨테이너 Ping 통신(실패)

ping -c 1 172.18.0.2
ping -c 1 172.18.0.3
ping -c 1 172.18.0.4

실습을 그대로 진행 시 계속해서 로컬 PC > 컨테이너로 Ping을 보낼 경우 실패가 발생하였다.

Packet Loss 발생 확인

원인이 불명확하여 해당 건에 대해서는 추후 docker 컨테이너 네트워크 구조에 대해 학습이 필요... 결국 각 kind(mgmt,prd,dev)에 ExtraPortMappings을 사용하여 Host PC에 IP를 통해 접속할 수 있게 환경을 구성하였따.

172.30.1.39 IP를 통해 바인딩 상태 확인

 

로컬 PC - ~/.kube/config 파일 수정

# config 파일 백업
cp ~/.kube/config ./kube-config.bak

# 수정
vi ~/.kube/config

# 확인
kubectl get node -v=6 --context kind-dev
kubectl get node -v=6 --context kind-prd

API 통신 IP 및 포트 확인
정상 호출 확인


ArgoCD - K8s 클러스터 등록(kind-dev, kind-prd)

# ArgoCD 현재 등록된 클러스터 확인
argocd cluster list
argocd cluster list -o json | jq

# 초기 SA 확인
k8s2 get sa -n kube-system
k8s3 get sa -n kube-system

# dev k8s 등록 및 SA(argocd-manager) 생성 확인
argocd cluster add kind-dev --name dev-k8s
k8s2 get sa -n kube-system argocd-manager

# prd k8s 등록
argocd cluster add kind-prd --name prd-k8s --yes
k8s3 get sa -n kube-system argocd-manager

# 클러스터 리스트 확인 및 조회
argocd cluster list
argocd cluster list -o json | jq
더보기
더보기
더보기
# 초기 SA 현황
NAME SECRETS AGE
attachdetach-controller 0 3m37s
bootstrap-signer 0 3m41s
certificate-controller 0 3m40s
clusterrole-aggregation-controller 0 3m38s
coredns 0 3m42s
cronjob-controller 0 3m41s
daemon-set-controller 0 3m41s
default 0 3m36s
deployment-controller 0 3m41s
disruption-controller 0 3m41s
endpoint-controller 0 3m37s
endpointslice-controller 0 3m39s
endpointslicemirroring-controller 0 3m40s
ephemeral-volume-controller 0 3m41s
expand-controller 0 3m38s
generic-garbage-collector 0 3m39s
horizontal-pod-autoscaler 0 3m37s
job-controller 0 3m41s
kindnet 0 3m41s
kube-proxy 0 3m42s
legacy-service-account-token-cleaner 0 3m39s
namespace-controller 0 3m40s
node-controller 0 3m41s
persistent-volume-binder 0 3m38s
pod-garbage-collector 0 3m37s
pv-protection-controller 0 3m39s
pvc-protection-controller 0 3m40s
replicaset-controller 0 3m41s
replication-controller 0 3m41s
resourcequota-controller 0 3m38s
root-ca-cert-publisher 0 3m38s
service-account-controller 0 3m41s
statefulset-controller 0 3m40s
token-cleaner 0 3m40s
ttl-after-finished-controller 0 3m37s
ttl-controller 0 3m41s
validatingadmissionpolicy-status-controller 0 3m39s

dev 클러스터 등록
prd 클러스터 등록
argoCD - 클러스터 리스트 확인
prd,dev kind 클러스터 등록 확인
ArgoCD - URL 접속 확인


ArgoCD - 각 클러스터 NGINX 배포

# argocd app 배포
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: mgmt-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values.yaml
    path: nginx-chart
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: mgmt-nginx
    server: https://kubernetes.default.svc
EOF

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-dev.yaml
    path: nginx-chart
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: dev-nginx
    server: https://172.30.1.39:6445
EOF

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prd-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-prd.yaml
    path: nginx-chart
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: prd-nginx
    server: https://172.30.1.39:6446
EOF

 

ArgoCD - 애플리케이션 배포 확인

# 애플리케이션 조회
argocd app list

# mgmt
kubectl get pod,svc,ep,cm -n mgmt-nginx
curl -s http://127.0.0.1:30000

# dev
kubectl get pod,svc,ep,cm -n dev-nginx --context kind-dev
curl -s http://127.0.0.1:31000

# prd
kubectl get pod,svc,ep,cm -n prd-nginx --context kind-prd
curl -s http://127.0.0.1:32000

kind-mgmt 애플리케이션 배포 확인
kind-dev 애플리케이션 배포 확인
kind-prd 애플리케이션 배포 확인

ArgoCD - 애플리케이션 삭제

kubectl delete applications -n argocd mgmt-nginx dev-nginx prd-nginx

ApplicationSet

App of apps 패턴

App of Apps 패턴은 ArgoCD 환경에서 애플리케이션을 배포하는 애플리케이션을 의미

즉, 하나의 Root ArgoCD Application 리소스가 다른 하위에 ArgoCD Application 리소스들을 정의하고 동기화하는 역할을 수행합니다.

  • app of apps 패턴은 부모 애플리케이션과 자식 애플리케이션 집합을 논리적으로 그룹화할 수 있는 기능을 제공한다.
  • 부모 애플리케이션을 통해 자식 애플리케이션을 만들수 있고, 함께 배포할 수 있는 애플리케이션 그룹을 선언적으로 관리할 수 있다.
  • app of apps 패턴을 사용하면 여러 Argo CD 애플리케이션 CRD를 배포하는 대신 부모 애플리케이션 하나만 배포하면 된다.
  • 논리적 그룹화가 되어, 그룹의 모든 애플리케이션이 배포되고 정상 상태인지 확인 할 수 있다.

 

최상위 애플리케이션 (Root App)

- 단일 Git 저장소를 참조

- 해당 Git 저장소에는 배포하고자 하는 모든 하위 애플리케이션의 ArgoCD Application 정의 파일만 포함되어 있다.

- 해당 Root Appdms 정의된 파일들을 읽어 Kubernetes 클러스터에 배포하고, 여러 개의 하위 ArgoCD Apllication이 생성됨

 

하위 애플리케이션(Child Apps)

- Root App에서 생성된 애플리케이션은 실제 워크로드를 정의하는 Helm 차트, Kustomize 구성, Kubernetes Manifest 파일이 있는 각각의 Git 저장소를 특정 경로로 참조

- 실제 Kubernetes 리소스를 클러스터(kind-dev, kind-prd)에 배포하고 관리

 

App of apps 패턴의 장점

- 중앙 집중식 : 모든 애플리케이션의 배포 상태 및 구성을 단일 Git 저장소 (Root App이 참조하는 곳)를 통해 중앙에서 관리 가능

- 재사용 및 일관성 : 애플리케이션이 등록된 환경(Mgmt/Dev/Prod)에 표준화된 템플릿을 적용하기 용이

 

ArgoCD - Sample Application Deploy

argocd app create apps \
    --dest-namespace argocd \
    --dest-server https://kubernetes.default.svc \
    --repo https://github.com/gasida/cicd-study.git \ # gasida repo 활용
    --path apps

ArgoCD - Sample Application Sync

dargocd app sync apps

 

ArgoCD - Sample Application 상태 확인

argocd app list
kubectl get pod -A

sync 상태 확인

 

ArgoCD - Sample Application 삭제

argocd app delete argocd/apps --yes

ApplicationSet 컨트롤러

1. 해당 그림처럼 ApplicationSet Controller는 ArgoCD와 통신하는 목적은 애플리케이션에 생성,수정,삭제를 통해 선언적 상태를 정의된 상태로 보장 하는 것이 유일한 목표이다.

 

ApplicationSet은 Generator를 사용해 다른 데이터 소스를 지원하느 매개변수를 생성한다.

 

아래 링크는 ApplicationSet의 Generator에 대한 설명이다.

https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators/

 

 

 

 

 

 

ApplicationSet Generator 이름 용도
List Argo CD 애플리케이션이 사용할 수 있는 쿠버네티스 클러스터 목록
Cluster Argo CD에서 이미 정의되고 관리되는 클러스터 기반의 동적 목록 (클러스터 추가/제거 이벤트에 자동으로 응답하는 기능 포함)
Git 깃 리포지터리 내에서 혹은 깃 리포지터리의 디렉터리 구조를 기반으로 하는 애플리케이션 생성
Matrix 2개의 서로 다른 제너레이터에 대한 매개변수 결합
Merge 2개의 다른 제너레이터에 대한 매개변수 병합
SCM 공급자 소스 코드 관리 SCM Source Code Management 공급자(예, 깃랩이나 깃허브)에서 리포지터리를 자동으로 검색
Pull Request SCMaaS 제공자(예: GitHub)의 API를 사용하여 Repo 내에서 열려 있는 풀 리퀘스트를 자동으로 검색
Cluster Decision Resource 사용자 지정 리소스별 논리를 사용하여 배포할 Argo CD 클러스터 목록을 생성
Plugin 플러그인 생성기는 매개변수를 제공하기 위해 RPC HTTP 요청

ApplicationSet - List Generator 실습

- kind-dev, kind-prd ApplicationSet Deploy

# argocd app 배포(dev,prd)
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
  - list:
      elements:
      - cluster: dev-k8s
        url: https://172.30.1.39:6445 # ExtraPortMappings(각자 host IP)
      - cluster: prd-k8s
        url: https://172.30.1.39:6446 # ExtraPortMappings(각자 host IP)
  template:
    metadata:
      name: '{{.cluster}}-guestbook'
      labels:
        environment: '{{.cluster}}'
        managed-by: applicationset
    spec:
      project: default
      source:
        repoURL: https://github.com/gasida/cicd-study.git
        targetRevision: HEAD
        path: appset/list/{{.cluster}}
      destination:
        server: '{{.url}}'
        namespace: guestbook
      syncPolicy:
        syncOptions:
          - CreateNamespace=true
EOF

 

- Sync

argocd app sync -l managed-by=applicationset

 

ApplicationSet - Label 필터링

 

ApplicationSet - kind-dev, kind-prd 각 Pod 정보 확인

k8s2 get pod -n guestbook
k8s3 get pod -n guestbook

ApplicationSet - List Generator 실습 종료(삭제)

argocd appset delete guestbook --yes

ApplicationSet - Cluster Generator 실습

clusters: {}는 ArgoCD에 미리 등록된 모든 클러스터 목록을 가져오도록 지시합니다. 이 목록은 현재 ArgoCD 서버가 실행 중인 클러스터(In-Cluster, kind-mgmt)와 ArgoCD에 추가로 연결된 외부 클러스터(kind-dev,kind-prd) 모두 포함

 

(외부 클러스터를 가져오는건 ArgoCD에 등록된 클러스터 정보를 읽음으로 가져올 수 있음)

 

-ApplicationSet Deploy

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
  - clusters: {}
  template:
    metadata:
      name: '{{.name}}-guestbook'
      labels:
        managed-by: applicationset
    spec:
      project: "default"
      source:
        repoURL: https://github.com/gasida/cicd-study
        targetRevision: HEAD
        path: guestbook
      destination:
        server: '{{.server}}'
        namespace: guestbook
      syncPolicy:
        syncOptions:
          - CreateNamespace=true
EOF

# 확인
argocd appset list

 

- ApplicationSet Sync

argocd app sync -l managed-by=applicationset

 

- ApplicationSet 및 Pod 정보 확인

# 생성된 application yaml 확인
kubectl get applications -n argocd in-cluster-guestbook -o yaml | k neat | yq
kubectl get applications -n argocd dev-k8s-guestbook -o yaml | k neat | yq
kubectl get applications -n argocd prd-k8s-guestbook -o yaml | k neat | yq

# 각 k8s 에 배포된 파드 정보 확인
k8s1 get pod -n guestbook
k8s2 get pod -n guestbook
k8s3 get pod -n guestbook

 

동작 순서

ArgoCD(In-Cluster) > ApplicationSet > Child Application > 하위 클러스터(Dev, Prd) 실행

 

- ApplicationSet  실습 종료

argocd appset delete guestbook --yes