작년에 staging 배포를 argo로 전환한 후 여러가지 이슈를 해결하고(바쁘기도 했고) 최근에 드디어 상용쪽도 전환하였다. 👏👏
저번글에서 몇가지 이슈사항 및 주의할점을 적었는데, 중요한 한가지를 빠트린것 같아 또 글을 쓰게 되었다.
GitOps repo directories
일단 현 구성을 간략히 얘기하자면 하나의 applicationset에서 여러개의 application을 관리할 수 있도록 App of Apps 패턴을 사용하였고, gitops repo의 구성은 다음과 같다.
app은 각 서비스에 대한 리소스들(deployment, service, hpa, configmap 등)을 넣어 두었고 common은 여러개의 서비스가 함께 사용하는 리소스들(ingress, limit-range, quotas 등)을 넣어두었다.
applicationset의 여러 generator 들 중에서 git generator를 채택하였는데, 새로운 app이 추가될 때마다 git repo에만 추가하면 되고 applicationset을 별도로 수정하지 않아도 되어서 선택하였다. 그리고 각 application의 이름도 디렉토리의 path 기반으로 설정할 수 있어 좋았다. applicationset은 cluster별로 구성하였기 때문에 위의 repo 구성도 각 클러스터별로 나누어졌다. 즉 상용 기준으로 우리는 세개의 클러스터가 존재하기 때문에 applicationset, project도 세개씩 만들어 주었다.
Applicationset.yaml
project는 특별할 것 없고, applicationset은 다음과 같이 사용하고 있다.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: v1-appset
namespace: argocd
spec:
syncPolicy:
preserveResourcesOnDeletion: true
generators:
- git:
repoURL: [GITOPS_REPO_URL]
revision: master
directories:
- path: "apps/app/prod/v1/*"
- path: "apps/common/prod/v1/*"
template:
metadata:
annotations:
notifications.argoproj.io/subscribe.on-sync-failed.slack: [SLACK CHANNEL]
notifications.argoproj.io/subscribe.on-health-degraded.slack: [SLACK CHANNEL]
name: '{{path.basename}}'
spec:
project: v1-project
source:
repoURL: [GITOPS_REPO_URL]
targetRevision: master
path: '{{path}}'
destination:
name: [K8S_CLUSTER_NAME]
namespace: default
syncPolicy:
#automated:
# prune: true
# selfHeal: true
syncOptions:
- validate=false
- ApplyOutOfSyncOnly=true
#- prune=true
retry:
limit: 2
backoff:
duration: "10s"
factor: 2
maxDuration: "3m0s"
revisionHistoryLimit: 5
간략하게 설명해보자면 spec.generators[0].git.directories 아래에 있는 경로들을 application으로 만들어 주고 application의 이름은 path.basename. 즉 * 에 해당하는 디렉토리 이름이 된다.
git generator에 대해 더 자세한 내용을 알고싶다면 공식문서를 찾아보자!
source와 destination은 어디(깃 레포)에서 어디로(어떤 클러스터로) 배포할건지 정의하는 것이고
syncPolicy에서 다양한 옵션을 사양할 수 있다. 처음에는 autosync를 사용하였지만 이슈로 인해 수동으로 전환하였다.
(이슈는 저번글에서 작성하였음!)
sync에 실패할 경우 재시도할 수 있도록 재시도 로직을 추가하였고 application의 버전을 5개까지만 저장할 수 있도록 설정해두었다.
상단의 spec.syncPolicy.preserveResourcesOnDeletion 옵션이 내가 글을 쓰게 한 옵션이다.
서론이 길었는데 이제 왜 이 글을 쓰게 되었는지 얘기해 보려고 한다.
이슈 발생
kubernetes manifests 파일의 위치가 변할때 만약 directory가 통째로 이동된다면 파일만 이동한것임에도 applicationset은 application을 먼저 삭제한 후 생성하기에 실제 쿠버네티스 리소스가 삭제되버리는 현상이 발생하였다.
즉, 예를들어 apps/common/staging/v2/hpa 에 있던 hpa 파일들을 모두 각 서비스들 아래로 이동했을때 (apps/app/staging/v2/[SERVICE_NAME] 와 같은 위치) hpa 아래 파일들은 모두 이동되어 hpa 디렉토리가 사라지게 된다.
사실 파일이 삭제된것이 아니라 이동한것 뿐인데 applicationset에서 hpa라는 디렉토리(즉 하나의 application 이었음)가 삭제되었다고 판단하여 hpa들을 모두 삭제해버린다. 그리고 3분 주기(이때는 autosync였음. 만약 autosync가 아니라면 outofsync로 남아있음)때 이동된 hpa들이 재생성 된다. hpa정도야 서비스에 문제가 없는데 만약 deployment였다면? 아니면 alb ingress controller 를 사용하는 ingress였다면? 상상만으로도 아주 아찔하다. 😱
이슈 해결을 위한 시도
처음에는 해당 이슈를 해결하기 위해 syncOptions의 prune/pruneLast 를 테스트 해보았으나 원하는 결과가 나오지 않았다.
그러다가 application을 삭제할때 non-cascading로 삭제하면 application이 삭제되어도 실제 k8s 리소스는 삭제되지 않는것을 확인하였다.
하지만 우리는 App of Apps 패턴을 사용. 즉 applicationset이 해당 application의 삭제를 가만 바라만 보고 있지는 않는다. 수동으로 삭제하면 바로 재생성해버린다.
그래서 해당 옵션을 applicationset에 녹여낼 수 있을까 하다 찾아본것이 preserveResourcesOnDeletion 이었다. 해당 옵션은 syncPolicy 아래에 두라고 나와있어서 처음에는 spec.template.spec.syncPolicy 아래에 추가하고서는 왜 안되지 했었는데 spec.syncPolicy 아래에 추가해야한다.
해당 옵션을 적용한 후 applicationset을 배포하면 변화는 없으나 해당 applicationset으로 생성된 application에서 .metadata.finalizers 옵션이 없어진것을 볼 수 있다.
(ArgoCD는 preserveResourcesOnDeletion 이 false일때 해당 값을 설정한다.)
오른쪽이 preserveResourcesOnDeletion : true 옵션을 추가한 applicationset의 application 이다.
argocd는 applicationset이 삭제되면 해당 applicationset으로 생성한 application을 삭제하고, 그 application으로 생성한 kubernetes resources들을 삭제한다. 이러한 삭제를 cascading 삭제라고 하고, 이때 deletion finalizer에 의해 컨트롤 할 수 있다.
즉 오른쪽 사진에서 finalizers 가 없다는 것은 이러한 cascading 삭제에서 벗어났다는 것이고 (위의 사진 non-cascading 옵션과 같은 뜻이게됨) 그래서 application이 삭제되어도 실제 kubernetes 리소스는 삭제되지 않게 된다.
테스트 및 적용
해당 옵션을 설정한 다음에 이전과 같이 파일을 이동시키면서 디렉토리를 삭제해보았다.
apps/common/staging/v2/test/hpa.yaml -> apps/app/staging/v2/[SERVICE_NAME]/hpa.yaml
test 디렉토리 아래에는 하나의 파일만 존재했기 때문에 이동하면 test 디렉토리가 사라지고 이전에는 test 라는 application 이 삭제되면서 쿠버네티스 리소스(hpa.yaml)도 함께 삭제되었다.
test application은 삭제되지 않고 Error상태로 남았고
새로 파일이 추가된 application에서는 sync 작업 후 hpa가 조회되었다. 실제 hpa 가 재생성 되지도 않았다.
결론
argocd를 구축한 이후 이미 생성되어있는 리소스들의 yaml 파일들을 이동시킬때 혹시나 재생성되면 어떡하지 하는 두려움이 있었는데, 해당 옵션을 이용하여 안전하게 매니페스트 파일들을 옮길 수 있다. 나는 non-cascading 삭제로 접근하여 이러한 결과에 도달했는데 공식 문서에 보면 삭제방지로 해당 옵션을 소개하고 있다.(처음부터 이 문서봤으면 엄청 삽질하지는 않았을텐데ㅠ)
내가 설정한 옵션 외에 applicationset에서 create 만 할 수 있도록 policy를 설정하는 방법도 있으니 참고해보면 좋을 것 같다.
(dry-run 은 테스트할때나 써볼 수 있을듯)
참고 :
https://argocd-applicationset.readthedocs.io/en/stable/Getting-Started/
Getting Started - ApplicationSet Controller
Getting Started This guide assumes you are familiar with Argo CD and its basic concepts. See the Argo CD documentation for more information. Requirements Installed kubectl command-line tool Have a kubeconfig file (default location is ~/.kube/config). Insta
argocd-applicationset.readthedocs.io
Controlling Resource Modification - ApplicationSet Controller
Controlling if/when the ApplicationSet controller modifies Application resources The ApplicationSet controller supports a number of settings that limit the ability of the controller to make changes to generated Applications, for example, preventing the con
argocd-applicationset.readthedocs.io
'CI CD > argocd' 카테고리의 다른 글
[ArgoCD] argocd v.2.8이상 사이드카로 Vault Plugin 적용하기 (1) | 2023.10.09 |
---|---|
[argocd] argoCD로 배포 분리하기. 관련 주의할 점 및 이슈사항 정리 (0) | 2023.03.18 |