본문 바로가기

Infra & Devops

[Kubernetes] ArgoCD 정리 (1) - GitOps Repo 구성과 ArgoCD 설치

들어가며

 EKS 기반 쿠버네티스(이하 K8S) 환경 구축에 대해 다뤘던 지난 포스트를 작성한지 대략 7개월이 지나버렸다. 사실 해당 포스트는 2월부터 시작했던 신규 프로젝트에 적용할 K8S의 PoC를 위해 준비하는 과정에서 정리했던 것이다. 그리고 앞으로 K8S에 대해 학습 및 실무를 통해 알게되는 것들을 추가적인 포스팅을 통해 정리하려고 했으나.. 규모가 작지 않은 서비스 하나를 백지에서부터 단 6개월만에 시장에 릴리즈해야했던 상황속에 나의 에너지가 거기에 미치지 못했던것 같다.

 

 어쨌거나 지금은 서비스가 오픈 되었고, 추가 feature와 이벤트 및 프로모션으로 인해 개발업무는 여전히 많지만 포스팅을 더 이상 미룰 수는 없다고 생각했다. 그래서 CI/CD 기반 구축에 있어 내가 주도적으로 진행했던 것들 중에 상당히 효율/효과적이라고 생각하는 K8S 배포 도구, 'ArgoCD' 에 대해 정리하려고 한다. 아래는 개인적으로 느낀 장점들이다. 

 

  • UI가 깔끔하다.
  • 꼭 필요한 기능만 제공되기에 복잡한 설정이 필요하지 않다.
  • GitOps 방식을 매우 잘 따른다.
  • Deployment Rollout Status가 시각적으로 잘 표현된다.
  • 각 Worker Node의 리소스 사용량을 볼 수 있다.
  • 각 Pod에 배포된 Container 내부에서 Stdout 으로 출력되는 실시간 로그 확인이 가능하다.
  • History 기반 Rollback 이 쉽다.

 써보진 않았지만 'JenkinsX' 나 'Tekton' 같은 비슷한 도구들도 같거나 더 추가적인 장점이 있을지도 모르겠다. 하지만 GitOps 방식으로 프로세스를 진행하기 어렵다거나, 복잡한 설정이 필요하다는 이야기가 있어 도구 선정에서 제외되기는 했다. 이러한 판단에는 이 글이 도움이 되었다.

 

이제 계속 언급한 'GitOps' 를 시작으로 차근차근 알아보도록 하자.

 

GitOps 간단 정리

 먼저 알아볼 'GitOps' 라는 개념은 'Weaveworks' 사(社)가 처음 만든 용어로 알려져 있다. 이것은 말 그대로 형상 관리 도구인 'Git' 을 통해 개발자에게 익숙한 방식으로 인프라 또는 어플리케이션의 선언적인 설정파일을 관리하고 배포하는 일련의 프로세스를 말한다. 사실 'IaC(Infrastructure as a Code)' 개념을 서포트하는 도구들 즉, Terraform, Ansible, Peppet, Chef (이중 실무에서 접해본건 Ansible 뿐이지만..) 등은 선언적인 코드로 작성되어 관리되기때문에 이러한 'GitOps' 개념에 걸맞는다고 볼 수 있을 것 같다.

 

 ArgoCD를 사용하여 이러한 GitOps 프로세스를 통해 K8S 클러스터 내부로 어플리케이션(도커 이미지)이 배포되는 과정은 아래와 같다.

 

 

 

출처: https://www.gspann.com/resources/blogs/continuous-delivery-for-kubernetes-with-gitops-and-argo-cd/

 

 위 프로세스에서는 2가지의 Git repository가 운용된다. 하나는 어플리케이션의 코드베이스를 담고 있는 'Source repo'와 위에서 말한 배포 설정파일을 담고 있는 'GitOps repo' 가 그것이다. 지금은 K8S를 다루고 있으니, 'Deployment'를 비롯한 K8S 리소스의 각종 manifest 파일들이 'Kustomize' 기반으로 구성되어 해당 repo에서 관리될 것이다.

 

위 도식을 바탕으로, 현재 내가 담당하고 있는 서비스에서 수행되고 있는 배포 프로세스는 다음과 같다.

 

  1. 특정 마이크로서비스의 변경사항이 Source repo에 commit 된다.
  2. Jenkins의 Build Job이 해당 변경사항을 감지하고 Docker Image 빌드를 수행한다.
  3. 빌드의 결과물은 Amazon ECR Repository에 Push된다. (이미지 Tag는 Jenkins의 BuildNumber)
  4. 해당 Tag를 kustomize 명령어로 GitOps repo의 deployement manifest에 업데이트한다.
  5. 4단계에서 업데이트한 deployment의 변경사항이 GitOps repo에 commit 된다.
  6. GitOps를 향해 polling을 수행하고 있던 ArgoCD가 변경사항을 감지하고 Synchronize(배포)를 수행한다.

 이렇게 나열하고 보니 뭔가 복잡해 보이는 것 같기도 하다. 하지만 말이 좀 장황할뿐, 실제 경험해 본다면 굉장히 직관적이고 효과적인 배포 방법이라는 것을 느낄 것이라 생각한다. (장황하게 늘여쓴 각 Step은 추후에 리팩토링(?)해야할 것 같다..)

 

이제 'GitOps Repo' 가 어떻게 구성되는지 정리하겠다.

 

 Kustomize 기반 GitOps Repository 구성

 'GitOps repo'는 위에서 언급한 'Kustomize' 라는 도구가 작동하기 위한 구조로 관리된다. 'Kustomize'를 간단히 설명하자면, 각종 K8S 리소스를 선언형(참 선언형이라는 말 많이 쓴다..)으로 관리할 수 있게 해주는 도구이다. 물론 오리지널 K8S 리소스 Manifest 또한 선언형이라고 할 수 있지만, 개인적으로는 거기에 더 유연성을 더해주는 도구가 'Kustomize' 라고 이해했다.

 

 예를 들어보자. 현재 내가 담당하고 있는 서비스는 develop, qa(QA를 진행하는 환경), production 이라는 3가지 환경으로 운영되는데, 각각 환경마다 Pod 레플리카 갯수, Pod의 resource 설정(requests, limit), Service Account의 사용여부, ECR Repository 주소 등 많은 요소들이 다르게 구성된다. 'Kustomize'를 사용하면 이러한 설정들을 각환경마다 선언적으로 달리 적용할 수 있다. 아래는 'overlays' 방식으로 구성된 간단한 예시이다.

 

/gitops-repository % tree .
.
├── README
└── user-api
    ├── base
    │   ├── deployment.yaml
    │   └── kustomization.yaml
    ├── dev
    │   ├── deployment.yaml
    │   └── kustomization.yaml
    ├── prd
    │   ├── deployment.yaml
    │   └── kustomization.yaml
    └── qa
        ├── deployment.yaml
        └── kustomization.yaml

 

직관적으로 알 수 있겠지만 base 디렉토리의 내용은 모든 overlay 들에 공통적으로 적용되는 설정이다. 그리고 각각 디렉토리는 `kustomization.yaml` 파일이 필수적으로 위치한다. `base/deployment.yaml` 파일의 내용을 보자

 

apiVersion: apps/v1
kind: Deployment
metadata:
 name: user-api
spec:
 minReadySeconds: 10
 progressDeadlineSeconds: 180
 selector:
  matchLabels:
   app: user-api
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 0
  type: RollingUpdate
 template:
  metadata:
   name: user-api
   labels:
    app: user-api
  spec:
   containers: 
   - name: user-api
     image: user-api-image
     readinessProbe:
       periodSeconds: 1
       httpGet:
        path: /health
        port: 8080

 

이어지는 것은 `base/kustomization.yaml` 의 내용이다.

 

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml

 

위 Deployment manifest 에는 Pod 레플리카 갯수, 그리고 `containers` 설정 아래에 pull 할 image 주소가 누락되어 있다. 눈치 챘겠지만 이는 각 overlay의  설정에 추가되어 있다. 이제 `dev/deployment.yaml` 내용을 살펴보자.

 

apiVersion: apps/v1
kind: Deployment
metadata:
 name: user-api
spec:
 replicas: 1
 template:
  spec:
   serviceAccountName: user-api
   containers:
    - name: user-api
      command: ["/bin/sh","-c", "java -jar -Duser.timezone=Asia/Seoul -Dspring.profiles.active=dev user-api.jar"]

 

여전히 image 주소는 누락되어 있다. 이 부분은 빌드시마다 변경되는 Image tag 번호를 Kustomize 명령어로 업데이트 하기 때문에 `dev/kustomization.yaml` 에 아래와 같은 설정을 통해야한다.

 

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patchesStrategicMerge:
- deployment.yaml
resources:
- ../base
images:
- name: user-api-image
  newName: <iam account number>.dkr.ecr.ap-northeast-2.amazonaws.com/dev-user-api
  newTag: "276"

 

현재 적용되어 있는 Image Tag 번호는 '276' 이다. 이는 위에서 말했듯 Jenkins BuildNumber 이고,  Jenkins 에서 `kustomize edit set image` 명령어를 통해 업데이트 된다. 자세한 것은 이후 포스트에서 다루겠다.

 

여튼, 위 디렉토리 구조의 설정들을 기반으로 `kustomize build ./user-api/dev` 명령어를 수행하면 아래와 같이 Fully Deployment Manifest 설정이 산출되고, 이 내용이 최종적으로 K8S 클러스터에 적용될 것이다.

 

$ kustomize build ./user-api/dev
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-api
spec:
  minReadySeconds: 10
  progressDeadlineSeconds: 180
  replicas: 1
  selector:
    matchLabels:
      app: user-api
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: user-api
      name: user-api
    spec:
      containers:
      - command:
        - /bin/sh
        - -c
        - java -jar -Duser.timezone=Asia/Seoul -Dspring.profiles.active=dev user-api.jar
        image: <iam account number>.dkr.ecr.ap-northeast-2.amazonaws.com/dev-user-api:276
        name: user-api
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          periodSeconds: 1
      serviceAccountName: user-api

 

Kustomize 는 이외에도 다양한 기능이 있는데, 공식문서가 한글로 아주 잘 정리되어 있어 참고하면 많은 도움이 될것 같다.

 

ArgoCD 설치 및 접속

이제 위의 GitOps Repo와 직접적으로 소통할 ArgoCD를 K8S 클러스터 내부에 설치해보자. 설치 방법은 이미 많은 블로거들이 정리했고, 의외로 간단하다.

 

1) argocd cli 설치 (homebrew 기준)

$ brew tap argoproj/tap
$ brew install argoproj/tap/argocd

 

설치과정에 필요한 argocd cli 도구를 로컬에 설치한다.

 

2) namespace 생성 및 K8S 리소스 배포

$ kubectl create namespace argocd
 
## 위 namespace에 argocd 관련 리소스 적용
$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

 

설치하고자 하는 K8S 클러스터에 namespace를 생성하고, argocd 관련 통합 manifest 들을 `kubectl apply` 를 통해 배포한다.

 

3)  argocd 접속용 loadbalance 를 외부로 노출

$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

 

배포된 'argocd-server' Service 리소스를 실제 접속을 위해 'LoadBalancer' 타입으로 변경한다. 위 명령어를 수행하면 AWS 상에 로드밸런서 AWS 리소스가 생성되고, 아래와 같이 그 Domain Name이 할당된 것을 확인 할 수 있다.

 

4) 비밀번호 변경

먼저 argocd secret 에 설정된 기존 비밀번호를 `ARGO_PWD` 라는 로컬 환경 변수에 저장한다.

 

$ ARGO_PWD=`kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d`

 

그리고 1단계에서 설치한 `argocd cli` 를 사용해 로그인한다. `$ARGOCD_SERVER` 차리에는 위에서 세팅한 LoadBalance의 Domain Name을 입력한다.

 

$ argocd login $ARGOCD_SERVER --username admin --password $ARGO_PWD --insecure
'admin:login' logged in successfully
Context '<some hash>.ap-northeast-2.elb.amazonaws.com' updated

 

그리고 역시나 `argocd cli` 를 사용해 비밀번호를 변경한다.

 

$ argocd account update-password
*** Enter current password: # ARGOCD_SERVER와 동일
*** Enter new password: # 변경할 비밀번호 입력
*** Confirm new password: # 변경할 비밀번호 재입력(확인)

 

5) 접속

Username에 'admin', 그리고 변경한 비밀번호를 입력하면 로그인 할 수 있다. 지금은 아무런 App 배포설정도 되어있지 않기 때문에 텅텅 비어있을 것이다. 본격적인 배포 설정은 분량 조절 실패로 다음 포스트로 미루어야 겠다. (언제가 될진 모르겠지만..)

 

 

참고:

https://www.redhat.com/ko/topics/devops/what-is-gitops

 

GitOps란?

GitOps는 Git를 선언적 인프라에 대한 단일 정보 소스(Source Of Truth, SOT)로 사용해 인프라 및 애플리케이션 구성을 관리하기 위한 일련의 사례입니다.

www.redhat.com

https://kubernetes.io/ko/docs/tasks/manage-kubernetes-objects/kustomization/