본문 바로가기

CI CD/argocd

[argocd] argoCD로 배포 분리하기. 관련 주의할 점 및 이슈사항 정리

우리 회사는 codepipeline(source-codebuild)를 이용하여 서비스 배포를 하고 있는데, codebuild 에서 이미지 빌드 및 deployment 배포까지 수행하고 있었다. 이러한 구성에서 다음과 같은 문제점이 있었다.

 

1. 소스코드가 변경되어 빌드가 필요한 경우가 아닌 kubernetes resource만 변경이 있을때 배포만 다시 해도 되는데 빌드까지 다시 수행해야했다. 서비스별로 빌드하는데 시간은 차이가 있겠지만 작은 작업임에도 재배포하는데 10분정도 걸리는 경우도 있었다.

2. 전체 서비스의 배포상태를 한눈에 관리하기 어렵다. 스테이징 파이프라인만 70개 넘개 존재하는데 각 서비스들을 배포상태를 확인하기 위해 파이프라인을 하나씩 살펴봐야한다. 

3. 누군가 kubectl 명령어 이용해서 쿠버네티스 매니페스트파일을 바로 수정했을때 잘못된 경우 누가 잘못했는지 트래킹 하기 어렵고 바로 복구되지 않는다.

 

이러한 이유로 argoCD를 구축하게 되었고, 오늘은 argoCD로 변경하면서 생긴 이슈들이나 buildspec.yml 변경에 대해 얘기해 보려고 한다.

일단 기존의 buildspec은 대략 아래와 같고 그중 post_build 단계에서 배포를 수행하였다.

version: 0.2
env:
  git-credential-helper: yes
phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      - $(aws ecr get-login --region $REGION --no-include-email)
      - docker pull $REPOSITORY_URI:latest
  build:
    commands:
      # Dockerfile 이미지 빌드
      - "docker build --cache-from $REPOSITORY_URI:latest
        -- build-arg ...
        - ...
  post_build:
    commands:
      - docker push --all-tags $REPOSITORY_URI
      - ...
      - aws eks --region $REGION update-kubeconfig --name $CLUSTER_NAME
      - sed -i "s/REPLACED_IMAGE/${IMAGE_TAG}/g" ${DEPLOYMENT_FILE}
      - sed -i "s/THIS_STRING_IS_REPLACED_DURING_BUILD/$(date)/g" ${DEPLOYMENT_FILE}      
      - kubectl apply -f ${DEPLOYMENT_FILE}
      - |
        ATTEMPTS=0
        ROLLOUT_STATUS_CMD="kubectl rollout status deployment/${DEPLOYMENT_NAME}"
        until $ROLLOUT_STATUS_CMD || [ $ATTEMPTS -eq 60 ]; do
            $ROLLOUT_STATUS_CMD
            ATTEMPTS=$((attempts + 1))
            sleep 10
        done

보면 알겠지만 기존에는 kubectl apply -f ${DEPLOYMENT_FILE} 로 배포하고 배포 상태를 확인하기 위해 kubectl rollout status 명령어를 수행하여 배포상태를 체크하였다. 만약 배포가 잘 안되는 경우 관리자들은 직접 deployment 상태를 조회할 수 있겠지만 개발자들은 codebuild에서 보여주는 메시지밖에 조회할 수 없다. (RBAC으로 개발자들에게 권한부여되어 있지 않음)

argoCD를 새로 구축한 후 kubectl apply 명령어는 걷어내고 매니페스트파일을 git에 푸쉬한 후 바로 적용하기 위해 webhook으로 직접 argocd sync 명령어를 수행하는 방식으로 변경하였다.

version: 0.2
env:
  git-credential-helper: yes
phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      - $(aws ecr get-login --region $REGION --no-include-email)
      - docker pull $REPOSITORY_URI:latest
  build:
    commands:
      # Dockerfile 이미지 빌드
      - "docker build --cache-from $REPOSITORY_URI:latest
        -- build-arg ...
        - ...
  post_build:
    commands:
      - bash -c "if [ /"$CODEBUILD_BUILD_SUCCEEDING/" == /"0/" ]; then exit 1; fi"
      - docker push --all-tags $REPOSITORY_URI
      - ...
      - sed -i -E "s/(REPLACED_IMAGE)|([[:digit:]]+-[[:alnum:]]{39,50})/${CODEBUILD_BUILD_NUMBER}-${CODEBUILD_RESOLVED_SOURCE_VERSION}/g" ${DEPLOYMENT_FILE}
      - sed -i -E "s/(THIS_STRING_IS_REPLACED_DURING_BUILD)|(^[MTWFS]+.+UTC.*[[:digit:]]{4,4})/$(date)/g" ${DEPLOYMENT_FILE}
      - git config --global user.email $DEVOPS_EMAIL
      - git add .
      - git commit -m "${APP_NAME} 수정. 이미지 태그번호 ${CODEBUILD_BUILD_NUMBER}-${CODEBUILD_RESOLVED_SOURCE_VERSION}"
      - while :; do git pull --rebase origin master && git push origin master; if [ $? -ne 0 ]; then sleep 1; echo "retry"; continue;  else  break;  fi; done
      ## app sync
      - export ARGO_USER=$(aws ssm get-parameters --name "stg.argocd_user" --with-decryption --query "Parameters[0].Value" --output text)
      - export ARGO_PASSWORD=$(aws ssm get-parameters --name "stg.argocd_pwd" --with-decryption --query "Parameters[0].Value" --output text)
      - export ARGO_SERVER=$(aws ssm get-parameters --name "stg.argocd_server" --with-decryption --query "Parameters[0].Value" --output text)
      - echo ${ARGO_USER} ${ARGO_SERVER}
      - while :; do export ARGO_TOKEN=$(curl ${ARGO_SERVER}/api/v1/session -d '{"username":"'$ARGO_USER'","password":"'$ARGO_PASSWORD'"}' | cut -d '"' -f 4) ; if [ $? -ne 0 ]; then sleep 5; echo "retry"; continue;  else  break;  fi; done
      - curl ifconfig.me
      - curl --request POST ${ARGO_SERVER}/api/v1/applications/${APP_NAME}/sync -H "Authorization:Bearer ${ARGO_TOKEN}"

변경하면서 고려했던 점 & 이슈 사항

1) 기존에는 이미지 태그를 변경하여 deployment 파일에 직접 쓰지 않았지만 argoCD에서 배포는 codebuild가 아닌 argoCD가 수행해야해서 정규식을 사용할 수 있도록 이미지 태그를 생성할때 규칙을 정했다. (deployment에 변경된 이미지를 적용하여 push 해 놓아야 argoCD가 읽어 배포할 수 있다.)

 👉 이미지 변경 방법으로 직접 태그 변경하여 push 하지 않고 다른 방법들도 있겠지만, 추후에 helm으로 변경하려는 생각도 하고 있었고 시간상 argocd Image Updater를 살펴보지 못했다.

 

2) 처음에는 그냥 git pull & git push 만 하였으나 하나의 레포에 수많은 deployment들이 push되니 (위에서 말했던 것 처럼 스테이징 환경 파이프라인은 70개가 넘고 배포 횟수도 빈번하다.) push 되기 전에 변경이 생길때 충돌이 생겨 제대로 push 되지 않는 이슈들이 생겼다.

 

[Container] 2022/11/23 07:43:44 Running command git pull
remote: 
remote: Counting objects: 8        
remote: Counting objects: 8        
remote: Counting objects: 8, done.        
From https://git-codecommit.XXXX
   f31a231..f338175  master     -> origin/master
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint: 
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint: 
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.

[Container] 2022/11/23 07:43:47 Command did not exit successfully git pull exit status 128
[Container] 2022/11/23 07:43:47 Phase complete: POST_BUILD State: FAILED
[Container] 2022/11/23 07:43:47 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: git pull. Reason: exit status 128

 👉 이 문제를 해결하기 위해 pull 방식을 변경하여 시도해보고 push -f origin 으로 강제 push도 해보고 clone 할때 bare로 클론도 해보았지만 결국 해결할 수 없었다. 썩 맘에 들지는 않지만 push될때까지 반복문을 돌리는것으로 해결하였다. 만약 동일한 이슈 발생했는데 우아하게 해결한 분이 있다면 팁좀 주세요😣🥲

 

3) argocd sync API를 사용할때 server domain이나 관리자 권한을 가진 계정 정보가 필요한데, 민감한 정보들을 코드빌드 환경변수나 빌드스펙에 넣지 않고 aws의 secret manager에 담아두고 가져와서 사용하게 했다. 나중에 정보가 변경될 일이 있을때 빌드스펙 변경없이 모든 파이프라인에 한번에 적용시키기도 쉽다.

 

4) sync API 호출하기 전에 토큰을 가져와서 해당 토큰을 헤더에 넣어 호출해야한다. secret manager에서도 값을 잘 가져오고, 하드코딩하여 api/v1/session API 호출하였을때도 잘 수행 되는데 변수로 지정하여서 호출할때만 안되어서 엄청 애먹었다.

 👉 변수로 가져와서 이용하려면 ' ' 안에 넣어주면 된다.

 💎 https://argo-cd.readthedocs.io/en/stable/developer-guide/api-docs/

 

API Docs - Argo CD - Declarative GitOps CD for Kubernetes

API Docs You can find the Swagger docs by setting the path to /swagger-ui in your Argo CD UI's. E.g. http://localhost:8080/swagger-ui. Authorization You'll need to authorize your API using a bearer token. To get a token: $ curl $ARGOCD_SERVER/api/v1/sessio

argo-cd.readthedocs.io

 

5) 이건 최근에 안 사실인데, autosync를 사용하고 있을때 sync API 호출이 안먹힌다. 즉 마지막에 있는 sync api가 호출한다고 바로 sync되지 않는다. 사실 이 글을 쓴 가장 큰 이유였고, 서비스상 문제되지도 않아서 argocd로 변경한 이후 계속 모르고 있었다..(사실은 autosync 주기인 3분마다 sync되고 있던것도 모르고..)

https://github.com/argoproj/argo-cd/issues/9830

 

Argo hook not running on auto sync only on manual sync · Issue #9830 · argoproj/argo-cd

Checklist: I've searched in the docs and FAQ for my answer: https://bit.ly/argocd-faq. I've included steps to reproduce the bug. I've pasted the output of argocd version. Describe the b...

github.com

여기 github Issue에도 올라와있고 나말고도 많은 사람들이 겪고 있었다. 하지만 이와 관련한 내용을 다루고 있는 블로그는 보지 못했다.

👉  어차피 outofsync인 경우 슬랙으로 알람 발송하도록 설정해 놓았기 때문에 기존처럼 빌드 후 바로 배포될 수 있도록 manual Apply로 변경하였다. (꼭 바로 배포하지 않아도 된다고 하면 autosync를 이용해도 될 것 같다. 개인적으로 sync주기를 줄이는 방법은 애플리케이션 수에 따라 argoCD에 부하를 많이 줄 수 있기때문에 추천하지 않는다.)

 

6) 이건 argo에서 주의할점은 아니고 codebuild를 사용하는 사람들은 겪을 수도 있는 문제인데, 우리는 현재 build 단계에서 도커 이미지 빌드를 수행하고 태그를 붙이고 post_build에서 이미지를 푸쉬한 후에 argo 배포를 진행하고 있다. 빌드가 성공하면 태그를 붙이는데, 실패하는 경우 태그를 붙이지 못하고 ecr에는 당연히 최근에 빌드한 이미지가 존재하지 않게된다. 그 상태에서 post_build 단계를 수행하는데, 방금 빌드한 이미지 태그값을 변수로 가지고 있다가 deployment에 덮어써버리니 없는 이미지를 당겨오려고 해서 ImagePullBackOff 에러가 발생하는 일이 있었다.

즉, 이런 상황이다.

1. build 실패가 발생한다. codebuild 결과가 Failed이라고 떠있는데 아래 post_build를 실행한다고 한다.

2. post_build에서 docker push를 수행하지만 build가 실패하여 새로운 이미지 태그가 붙지 않았다. (위의 빌드에서 생성된 태그는 9- 임)

3. 이미지가 없는데 deployment의 이미지는 변경이 된 후 push 되었고 그 상태에서 argocd sync를 호출한다.

4. deployment에 명시된대로 9-XXX 이미지를 가져오려하지만 존재하지 않기에 ImagePullBackOff 에러가 발생한다.

관련하여 찾아보니 POST_BUILD 단계는 BUILD 단계의 성공 유무와 상관없이 수행된다고 한다. 그래서 aws에서는 BUILD의 성공 유무를 판단하기 위해서 CODEBUILD_BUILD_SUCCEEDING 변수를 제공한다. 나는 이 변수를 이용하여 post_build 가장 첫 단계에 조건문을 추가하여 실패인 "0" 값이 반환될때 빌드를 종료하도록 추가하였다.

- post_build 단계에서 CODEBUILD_BUILD_SUCCEEDING 변수를 확인하여 실패인 경우 종료시킨다.

post_build:
  commands:
    - bash -c "if [ /"$CODEBUILD_BUILD_SUCCEEDING/" == /"0/" ]; then exit 1; fi"

이전과 동일하게 빌드가 실패하여도 그 이후 단계가 수행되지 않는다. 👏👏

 

argocd에 대해 검색해보면 argocd가 무엇이고 apply 관련하여 어떤 옵션이 있는지, 설치방법 등은 많이 나오는데 기존의 배포에서 변경할때 어떤 식으로 바꾸었는지, 고려해야할 점은 뭔지, 운영하다 발생하는 이슈들에 대해서는 잘 나오지 않는 것 같아 작성해보았다. 위에 정리한 내용들이 배포 변경 후 하루 아침에 알게되었던 내용이 아니라 운영한지 몇 주 지났을때 발견된 문제들도 있어 처음 argoCD로 변경하는 사람들에게 도움이 될거라 생각한다.

물론 오늘은 배포할때의 이슈에 대해 작성했지만 다음에 시간이 되면 argoCD 자체에서 발생했던 이슈에 대해 정리해볼까 한다. 당연히 한번에 잘 적용한 사람들도 많이 있겠지만 조금이라도 도움이 되었으면 좋겠다. 🙂

마지막은 argocd UI