ArgoCD 란 무엇이고 무엇을 하는가? (with 공식 도큐먼트)

ArgoCD 가 무엇인지 ArgoCD 공식 문서를 토대로 간단하게 정리한 글입니다. 그와 더불어서 ArgoCD 의 Application 원천으로 사용 가능한 Helm, Kustomize, Plane Kubernetes Manifests 가 무엇인지 설명하고, 그와 더불어서 ArgoCD가 이야기하는 베스트 프렉티스, App of Apps 에 대한 이야기도 간략하게 다룹니다.

ArgoCD 란 무엇이고 무엇을 하는가? (with 공식 도큐먼트)

ArgoCD?

ArgoCD는 쿠버네티스를 위한 선언적 GitOps, 지속적 배포 도구

ArgoCD는 원하는 애플리케이션 상태를 정의하기 위해 Git 레포지토리를 소스로 사용하는 GitOps 패턴을 따르고, 쿠버네티스 manifests 파일은 아래와 같은 여러 방법으로 정의 가능

  • kustomize application
  • helm charts
  • jsonnet files
  • Plain directory of YAML/json manifests
  • 그 외 플러그인을 통한 설정

아키텍쳐

 

ArgoCD 는 실행중인 애플리케이션을 모니터링하고, 현재 상태를 우리가 원하는 Desired 상태(Git repo 의 manifests 에 정의된 상턔)와 비교하는 쿠버네티스 컨트롤러로 구현되었습니다.

현재 상태가 목표 상태와 다른 경우 애플리케이션은 OutOfSync 상태로 간주합니다. 이러한 상태의 차이점을 시각화하고, 알림을 줄 수 있고, 원하는 상태로 자동 , 또는 수동으로 동기화 할 수 있는 기능을 제공합니다.

 

컴포넌트

아래는 쿠버네티스 클러스터에 ArgoCD 를 설치 했을 때 설치되는 ArgoCD 의 컴포넌트들입니다.

API Server

API Server 는 gRPC/REST 서버로, 웹 UI, CLI, CI/CD 시스템에서 사용하기 위한 API를 노출합니다. 이 서버는 아래와 같은 역할을 합니다.

  • 애플리케이션 매니지먼트와 상태 리포트
  • invoking of application operations (sync, rollback, user-defined actions)
  • repository and cluster credential management (stored ad K8s secrets)
  • authn(인증) 그리고 외부 인증 공급자 (idP)에 대한 authn 위임
  • RBAC 강제
  • listener/forwarder for git webhook events

Repository Server

레포지토리 서버는 애플리케이션 매니페스트를 저장하는 로컬 Git 레포지토리의 로컬 캐시를 관리하는 내부 서비스입니다. (Redis 사용 구현). 아래와 같은 inputs 가 제공되었을 때 쿠버네티스 매니페스트를 생성하고 반환하는 역할을 합니다.

  • Repository URL
  • Revision (Commit, tag , branch)
  • application path (Git 레포 안에서의 경로)
  • template specific settings: parameters, helm values.yaml 

Application Controller

애플리케이션 컨트롤러는 현재 상태와 원하는 상태를 지속적으로 비교하는 쿠버네티스 컨트롤러입니다. (컨트롤러 )

쿠버네티스 컨트롤러는 일종의 패턴으로, 하나 이상의 쿠버네티스 리소스 유형을 추적합니다. 이 컨트롤러 오브젝트는 현재 리소스의 상태를 의도한 상태에 가깝게 만드는 역할을 합니다. 

아무튼, 이 ArgoCD컨트롤러는 현재 실행중인 애플리케이션의 상태를 목표한 상태, 즉 ArgoCD 프로젝트에 기술한 Git Repo, Revision, Path 등을 종합해서, Git 에 저장된 Desired State(Manifest)와 비교하고, 만약 실행중인 애플리케이션과 목표한 상태가 다른 경우 OutOfSync 를 감지합니다. 사용자의 설정에 따라 자동으로 Sync 를 하거나, 메뉴얼하게 하거나 하는 등의 동작을 할 수 있고. 유저가 정의한 lifecycle에 대한 훅을 호출 할 수 있습니다. (PreSync, Sync, PostSync)

Core Concepts

Application

Manifests 로 정의된 쿠버네티스 리소스의 집합입니다. Application 은 ArgoCD CRD 입니다.

(argocdproj.io/v1alpha1)

e.g

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: test-httpbin
  namespace: test-argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    namespace: test-httpbin
    server: https://kubernetes.default.svc
  project: default
  source:
    path: dev/httpbin
    repoURL: https://github.com/foo/bar.git
    targetRevision: master
  syncPolicy:
    automated:
      prune: false
      selfHeal: false
    syncOptions:
    - CreateNamespace=true
    - ServerSideApply=true

Application source type

Application 에서 사용할 manifests 의 형태.

  • plain manifests (yaml, json)
  • kustomize
  • helm
  • etc..

Target state (Desired state)

Git 에 작성되어있는 우리가 원하는 애플리케이션의 상태.

각종 쿠버네티스 오브젝트들의 manifests 들을 의미하고, ArgoCD 가 Sync (동기화) 하며 도달하고자 하는 상태를 의미함

Live State

현재 애플리케이션(쿠버네티스 오브젝트들)의 상태. Live State 와 Target State 가 동일하면 Sync, 동일하지 않다면 OutOfSync 를 의미함

Sync status

우리가 원하는 상태와, 현재 상태의 차이를 의미함.

Sync

동기화. Target State 와 Live State 를 일치시키는 작업

기본적으로 3분마다 git repo 에 manifests 변경점이 있는지 polling 하는데. 이 딜레이를 제거하고싶은 경우에는 github 등으로부터 webhook을 받아 처리 가능. (단 argocd 접근 URL/도메인이 외부에 노출되어있어야 함)

Sync operation state

동기화 작업이 성공했는지 아닌지

Refresh

현재 상태와 원하는 상태(git)을 비교하는 작업.

Health

애플리케이션의 상태(Helath). 애플리케이션이 정상적으로 동작중인지 등

Tool

Manifest 를 디렉터리 내 파일로부터 만들어내기 위한 툴. Kustomize 같은것들을 의미


정리하자면

ArgoCD 는 GitOps 툴이고, Git repo url, revision, application path, values.yaml(helm) 등과 같은 정보를 토대로 Git 으로부터 Manifests 를 생성/얻어오고. 그 Manifests 파일을 통해 쿠버네티스에서 애플리케이션을 구동합니다. 그리고 현재 상태와 원하는 상태(Git)를 지속적으로 비교하고, OutOfSync 발생시 자동, 혹은 메뉴얼하게 다시 원하는 상태의 Manifests 파일을 쿠버네티스에 전달합니다.  

중요한 점은. ArgoCD 라는 명칭 답게 CI (쿠버네티스에서는 Container Image 생성, 그 외에는 뭐 앱 빌드 등)를 하는것은 아니기 때문에. Github Action 등으로 Git 에 PR merge 등이 발생하는 경우 → Container Image Build 와 같은 CI 과정은 별도로 있어야 한다는걸 유의해야합니다. (argocd best practice 에 별도로 명시되어있는 내용. 아래에 공유 예정)

또한, ArgoCD 는 애플리케이션의 소스코드 (.java, .py 등등)를 전혀 알필요도, 볼필요도 없이 오직 Kubernetes Manifests (yaml/json)에만 관심이 있다는 점도 유의해야합니다.

코드 개발 → Github merge → CI(이미지 빌드)

Github 에 Manifests 파일 변경 → ArgoCD OutOfSync → Sync → Manifests 반영

위 두 파이프라인은 전혀 다르지만, 어느정도 같이 가야한다는 점이 또 유의해야 할 점이라고 볼 수 있습니다.

또한 Kubernetes 를 위한 GitOps 툴이기 때문에, CD 를 하기 위해서는 해당 애플리케이션을 Containerizing 해야 한다는 것도 유의해야 합니다.

ArgoCD 의 Desired State 가 git에 있어야 하는데. 이 Desired State 는 Kubernetes Manifests 를 의미하고, 지원되는 것들에는 Kustomize, Helm, Plain Manifests, jsonnet, Any custom config management tool configured as a config management plugin(?)가 있다.  

Kustomize 는 kustomization.yaml로 불리는 최종 manifests 파일에 base, resources , patches 를 구분해서 넣을 수 있는데, 이것을 통해 형상별 base 기반으로 여러 값들을 바꿔가면서 overlay 형태로 여러 manifests를 만들 수 있습니다. (Kustomize로 K8S 리소스 관리하기 , https://bcho.tistory.com/1392)

(Kustoization 예제 : https://github.com/kimsehwan96/k8s-kustomize-example)

Helm 은 쿠버네티스 패키지 매니저이며, Helm Chart 는 헬름 패키지를 의미한다. 이 패키지에는 쿠버네티스 클러스터 내에서 애플리케이션, 도구, 서비스를 구동하는데 필요한 모든 리소스 정의가 포함되어있다. (Manifests) 


번외

 

Best practice

 

Manifests(Config) 레포와 소스코드(애플리케이션) 레포의 분리

Best Practices - Argo CD - Declarative GitOps CD for Kubernetes

쿠버네티스 Manifests를 관리하는 레포와, 애플리케이션의 소스코드 레포를 분리하는것은 아래와 같은 이유로 강력하게 추천됩니다.

 

  1. 애플리케이션 코드와 애플리케이션 설정(k8s manifests)를 확실하게 분리합니다. 만약 두개를 같은 레포에서 관리하고있다면, 자동회된 CI 단계의 수준에 따라서 다르겠지만(얼마나 잘 구축했는지). 단순히 replicas 개수를 바꾸는 커밋/푸시를 하는것임에도 애플리케이션 빌드를 하는CI 동작을 트리거하게 될 수도 있습니다. (단순 예시)
  2. audit log 를 더 깔끔하게 만듭니다. audit(감사)를 위해서 설정파일(manifests)을 들고있는 git 레포는 여러 변경점들에 대한 더 깔끔한 git 히스토리 관리가 필요합니다. 단순한 개발 목적의 커밋은 감사 목적의 히스토리 추적에 노이즈가 될 뿐입니다.
  3. 애플리케이션은 여러 Git 리포지토리에서 빌드된 서비스로 구성될 수 있지만 단일 단위로 배포됩니다. 마이크로서비스 애플리케이션은 서로 다른 버전 관리 체계와 릴리스 주기를 가진 서비스들로 구성되는 경우가 많습니다(예: ELK, Kafka + ZooKeeper). 매니페스트를 단일 구성 요소의 소스 코드 리포지토리 중 하나에 저장하는 것은 적절하지 않을 수 있습니다.
  4. 액세스 분리. 애플리케이션을 개발하는 개발자와 프로덕션 환경으로 푸시할 수 있거나 푸시해야 하는 개발자가 의도적이든 의도적이지 않든 반드시 동일하지 않을 수 있습니다. 리포지토리를 분리하면 애플리케이션 구성 리포지토리가 아닌 소스 코드 리포지토리에 커밋 액세스 권한을 부여할 수 있습니다.
  5. CI 파이프라인을 자동화하는 경우 매니페스트 변경 사항을 동일한 Git 리포지토리에 푸시하면 빌드 작업과 Git 커밋 트리거의 무한 루프가 트리거될 수 있습니다. 구성 변경 사항을 푸시할 별도의 리포지토리가 있으면 이러한 일이 발생하지 않습니다.

긴급 대응을 위한 여지를 만들기

일부 긴급대응/자동화를 위한 여지를 남겨두고 모든 것을 Git manifests 로 작성하지 않는것이 나을 수도 있습니다. 만약 Deployment 오브젝트의 replica 개수를 K8s Horizontal Pod Autoscaler(HPA)가 통제하도록 두고 싶은 경우, replicas를 git 에 넣지 않는것이 좋습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  # do not include replicas in the manifests if you want replicas to be controlled by HPA
  # replicas: 1
  template:
    spec:
      containers:
      - image: nginx:1.7.9
        name: nginx
        ports:
        - containerPort: 80
...

replica 는 Deployment 오브젝트에 넣지 않는것이 ArgoCD 와의 궁합이 더 좋다. HPA 가 관리하도록 하자.

Kustomize/Helm 을 사용시 업스트림 버전을 고정시키기

helm / kustomize 를 사용하는 경우, 이것들은 템플릿 도구이기때문에 base (kustomize 의 base 및 helm 의 업스트림 레포 등)의 변경에 따라서 manifests 가 변경 될 수 있습니다. (보통 외부 helm chart 등을 사용할 때 발생) 따라서 kustomize 의 외부 base나, 외부 helm chart 의 버전을 고정해서 사용하는것이 추천됨.

bases:
- github.com/argoproj/argo-cd//manifests/cluster-install?ref=v0.11.1

kustomization 예시

Kubernetes object

선언적 접근법을 통해 쿠버네티스 오브젝트를 만들 수 있다. 쿠버네티스 오브젝트는 파드, 디플로이먼트, 레플리카 셋, 서비스, 컨피그맵, 시크릿 등 쿠버네티스 내에서 영속성을 가지는 오브젝트. 이것에 대한 명세를 쿠버네티스에 제공하면, 쿠버네티스는 이러한 오브젝트에 대한 명세들을 etcd에 넣어둔다. 

Kustomize

https://github.com/kubernetes-sigs/kustomize/blob/master/examples/chart.md

kustomize 는 쿠버네티스 리소스 설정(manifests)를 템플릿과 DSL로부터 자유롭게 커스텀 할 수 있는 솔루션.

원본 yaml 을 건드리지 않고도 다양한 목적으로 커스텀 할 수 있고. make 나 sed 와 비슷한 동작을 합니다.

deployment, servic, configmap과 같은 yaml 파일이 있는곳에 kustomization.yaml 파일을 생성합니다. 그리고 이것들을 커스텀해서 새로운 manifests 를 생성 할 수 있습니다. (예를 들면 deployment, service 매니페스트에 공통 label 을 붙여준다던지)

example : GitHub - kimsehwan96/k8s-kustomize-example: for example

Helm

Helm | Helm

Plain Manifests

 

쿠버네티스에서 선언적 접근 방식으로 오브젝트를 생성/업데이트 할 수 있는 설정파일이다. 대부분 yaml 로 작성한다.

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

위는 그 예시. 한 파일안에 --- 를 통해 오브젝트(리소스)타입을 구분하여 선언 가능하나. 웬만하면 파일을 쪼개서 관리하는게 낫다. (안헷갈리게) 

순수한 manifests 파일

Helm with kustomize

namespace: test-argocd

resources:
  - ./argocd-namespace.yaml
helmCharts:
  - name: argo-cd
    includeCRDs: true
    repo: https://argoproj.github.io/argo-helm
    releaseName: argo-cd
    version: 5.42.1 # 2023.08.01 https://artifacthub.io/packages/helm/argo/argo-cd
    namespace: test-argocd
    valuesFile: values.yaml

kustomize 와 helm 을 같이 사용

kustomize 와 helm 을 같이 쓰는 방법이다. helm 을 CLI 기반으로 쓰기엔 히스토리추적이 어렵고 (궁극적인 gitops 하기엔 조금..), 순수 manifests 와 같이 쓸 수 있는(?) 방법인 helm with kustomize 방법이 여러모로 관리하기 편하다고 느껴진다.  

장점이라고하면 모든 manifest 를 kustomize 라는 단일 툴 기반으로 관리가 가능 하다. (helm 을 쓰든 , 안쓰든). 그리고 kubectl 에 네이티브하게 이식되어있다. ($ kubectl kustomize . | kubectl apply -f - , $ kubectl kustomize --enable-helm . | kubectl apply -f -)

Manual Deploy

우선 ArgoCD 에서의 Deploy 는 Sync 와 동일하다. 이 의미는 git 에 있는 manifests 의 상태로 argocd 가 destination namespace/cluster 에 해당 쿠버네티스 오브젝트들을 Desired state 로 만들어 내겠다는 의미이다.

이러한 특징 덕분에 2가지의 접근 방법이 발생하게 되는데. 우선 Containerizing 된 애플리케이션(자바, 파이썬, 웹 등)을 배포하는 케이스를 가정해보자.

이러한 애플리케이션은 보통 Deployment 오브젝트를 이용해 배포를 하게 될 것이다. (Deployment 오브젝트를 생성하면 내부적으로 Replicaset, pod 들도 생길거고, 그것과 별도로 Serivce 오브젝트등도 필요하겠지요. 그 외에 다른 오브젝트들도 필요할수도.)

이 때 컨테이너를 만들기 위해서. 파드에 올라갈 컨테이너들을 명시하는데. 컨테이너를 만들 이미지를 {imageName}:{tag} 형식으로 지정 할 수 있다.

예를들어 foo/backend:latest 라는 이미지를 기반으로 배포를 한다고했을 때. manifest 를 제외하고 Application 이 새로 빌드되고, latest 태그를 달아서 이미지가 푸시되었을 때. 실제로 기존에 latest 로 배포된 이미지와, 방금 latest 로 새로 배포된 이미지는 다른 이미지이다. (소스코드 변경점이 있는 경우) 하지만 ArgoCD 입장에서는 해당 태그에 새로운 이미지가 푸시되었든 어떻게 되었든 manifest 가 변경되지 않으면 변경점이 없기 때문에 새로 컨테이너들을 띄우지 않는다.  

이런 상황에서 새로운 latest 태그를 갖는 이미지로 컨테이너를 띄우고 싶다면 ArgoCD 레벨에서 할 건 없고.

$ kubectl rollout restart deployment {deployment name} 을 통해 재시작해야하는데. 심지어 여기서 imagePullPolicy 가 IfNotPresent 로 되어있으면 새로 이미지를 받아오지 않기 때문에 소용없고. imagePullPolicy 를 Always 로 하는 경우에만 반영이 될 것이다.

따라서 이미지 태그를 그대로 두면서 ArgoCD 를 통해 메뉴얼하든, 자동이든 배포하는것은 불가능에 가깝고.

만약 이미지를 foo/backend:v1.0.0 을 통해서 배포했다고 해보자. 이 경우에 애플리케이션 레포지토리에서 새로 v1.1.0 이미지를 빌드했고, 그것을 반영하고싶다면 애플리케이션 소스코드 레포지토리와 별도로. manifest 를 관리하는 레포에서 해당 이미지 태그를 v1.0.0 에서 v1.1.0 으로 변경하고 git 에 반영하면. ArgoCD 가 AutoSync가 활성화 되어있는 경우에 Webhook 을 연결했으면 즉시, 그게 아니라면 3분안에 변경점을 파악하고 앱들을 새로운 이미지로 띄우게 될 것이다.

만약 AutoSync 를 활성화 하지 않았다면 ArgoCD가 OutOfSync 를 보여줄것이고. (이것을 slack 에 noti 하는 것도 가능) 이 때 운영 담당자가 manifest 변경점을 확인하고, git 과 sync를 맞춰도 된다고 판단하면 그 때 Manual 하게 Sync를 맞추면 될 것이다.

어찌되었든, 애플리케이션 소스 레포지토리와 Manifest 레포지토리는 별개로 가져가고. 애플리케이션 소스 레포지토리의 CI 동작과 동시에 ArgoCD 와의 어떤 자동화된 작업을 하는것은 다양한 방법을 찾아봐야한다.  

예를들어 애플리케이션 소스코드 레포지토리에서 새로운 버전으로 빌드를 했고. 그것과 동시에 어떤식으로든 manifest 파일내 이미지 정보가 새로운 버전으로 바뀐다고 했을 때. 그리고 그것이 manifest 깃에 바로 반영된다고 했을 때.

 그렇게되면 또 CI 툴에서 컨테이너 이미지가 빌드되기 전에 ArgoCD 의 Sync 동작을 수행하면 ImagePullBackOff 에러가 발생할 것..

→ ArgoCD Image Updater 라는 소프트웨어가 있는데. 여러 조건이 있겠지만 Docker Image 가 업데이트되는것을 감지해서 새로운 이미지를 사용하도록 자동으로 git 에 버전과 관련된 변경점을 커밋해버리는것 같다. (더 알아볼 필요가 있음)

GitHub - argoproj-labs/argocd-image-updater: Automatic container image update for Argo CD

Argo CD Image Updater

Self Managed ArgoCD

 

ArgoCD의 manifest 파일의 변경점을 감지하고 Sync를 맞출 수 있도록. ArgoCD 자신의 manifest를 바라보는 ArgoCD Application 을 운영하는 것.

argocd 가 argocd 애플리케이션을 관리한다.

Cluster bootstrapping (App of apps or whatever)

 

ArgoCD 의 애플리케이션을 통해, 다른 ArgoCD 애플리케이션 여러개를 생성하도록 하는 패턴. 클러스터 부트스트래핑 용으로 많이 사용 함.

우리가 필수적으로 세팅해야하는 일부 쿠버네티스 오브젝트(ConfigMap)에 대한 것이나, 필수적으로 사용해야하는 서비스/소프트웨어를 세팅하는데 사용하면 됩니다.

App of Apps 패턴

코드 예제 : https://github.com/kimsehwan96/istio-example/tree/master/apps