본문 바로가기

IT

쿠버네티스[EKS] CICD review (ArgoCD+GitLab Runner)

728x90

쿠버네티스(Amazon EKS) 환경에서 CICD 파이프라인을 구축한 과정을 기록하려고 한다.

 

전체 파이프라인을 구성하는 과정에서 많은 블로그들을 참고하면서 작업했고 내 욕심에 따라 수정의 반복이였다. 
뭔가 GUI가 있었으면 좋겠고 권한이 잘 관리 되었으면 좋겠고 내가 작업한 결과물에 대해서 형상 관리가 되었으면 했다. 나중엔 GitOps라는 방식을 알게 되었고 결론적으로 ArgoCD를 도입하게 되었다.

 

처음엔 ECS로 컨테이너 오케스트레이션 환경을 구성했고 CICD 파이프라인을 구성하다보니 aws-cli를 통해 aws codedeploy 명령어로 CD를 작업하거나 aws ecs update 명령어로 CD를 구성하였다. EKS로 넘어오면서도 처음에는 ECS에서 구축한 것과 비슷한 방식으로 rollouts 명령 등을 활용해 배포 파이프라인을 구성했는데 위와 같은 요구 사항들로 인해 변경하게 되었다.

 

여기서 느낀 건 정답은 없다는 것이였다. 
니즈는 바뀌고 기술은 발전하고 나는 아직 모르는게 많다. 생각보다 기술, 서비스, 솔루션, 오픈소스 등이 어느정도 추상화되어있음을 느끼고 내 상황에 맞게 잘 활용하면 되는 것 같다.

단지 어떤 것을 개발하고 구성할 때마다 변경에 유연하게 대처할 수 있도록 고민해보는 시간이 중요한 것 같다.

 

전체 CICD 구성도는 다음과 같다.

 

EKS CICD 구성도

엔지니어를 대상으로 작성한 CICD 구성도이지만 눈에 잘 들어올 지 모르겠다. 
배포 순서에 번호를 부여하였으니 같이 참고하여 밑에 gitlab-ci.yaml을 참고하면 될 듯 하다.

 

전체 흐름에 대해 간략하게 설명하면 다음과 같다.
Jenkins와 같은 CI 도구로 Gitlab-Runner를 활용하였다. 가장 익숙했기에 스크립트를 빨리 만들고 수정할 수 있었다. 이 부분은 익숙한 CI 도구로 변경하여도 무리가 없다.

 

Runner의 Job Stage는 총 두 단계로 구성했다. 
첫번째는 소스를 빌드하여 실행 파일(JAR)을 만들고, 두번째는 도커이미지를 만들어 이미지 레지스트리에 업로드하는 것이다. 그 후 쿠버네티스 오브젝트들을 담은 파일에서 배포된 서비스의 이미지 태그를 수정하여 다시 gitlab에 푸시하는 과정이다. Runner의 작업은 그렇게 끝나고 나머지는 ArgoCD가 3분을 주기로 gitlab에 오브젝트 파일을 비교하여 sync를 맞춘다. 이렇게 CD가 이루어진다.

 

처음에는 Dockerfile에 소스를 빌드하는 부분(컴파일&패키징)을 포함하였지만 메이븐 .m2에 라이브러리 캐싱이 안되어 도커 이미지를 만들때마다 시간이 너무 오래걸렸다.
결국 삽질 끝에 빌드 과정과 도커 이미지 빌드 과정을 분리하였다. 
참고) https://sh970901.tistory.com/138

 

Gitlab-Runner .m2 Caching

https://sh970901.tistory.com/136 gitlab runner docker in docker 구조대표적인 CICD(Continuous Integration/Continuous Delivery) 도구들 (gitlab runner, github action, jenkins 등) 이 있지만 이 글에서는 gitlab runner를 사용해 구축하

sh970901.tistory.com

 

gitlab-ci.yml을 보면 더 이해가 쉬울 것 같다. 

필요한 부분을 제거한다고 제거한 것인데 위 구성도에서 필요한 부분만 참고하도록 하자.

stages:
  - source-build
  - docker-deploy

cache:
  paths:
    - $CI_PROJECT_DIR/envs
    - /root/.m2/repository

variables:
  MAVEN_OPTS: -Dmaven.repo.local=/root/.m2/repository
  CACHE_ENV_PATH: $CI_PROJECT_DIR/envs


before_script:
  - mkdir -p $CACHE_ENV_PATH/$CI_COMMIT_BRANCH

.build_template:
  script: &package
    - echo "1. 메이븐 빌드&패키지"
    - mvn -U clean package
    - echo "2. target 캐싱 (save)"
    - cp -R target $CACHE_ENV_PATH/$CI_COMMIT_BRANCH/

.deploy_template:
  script: &deploy
    - echo "1. target 캐싱 (restore)"
    - mv $CACHE_ENV_PATH/$CI_COMMIT_BRANCH/target .
    - echo "2. 이미지 빌드"
    - docker build -t $DOCKER_IMAGE_NAME:$CI_PIPELINE_ID .
    - docker tag $DOCKER_IMAGE_NAME:$CI_PIPELINE_ID $ECR_REPO_URI:$CI_PIPELINE_ID
    - echo "3. 저장소 업로드"
    - docker push $ECR_REPO_URI:$CI_PIPELINE_ID
    - docker rmi -f $(docker images -f "dangling=true" -q) || true
    - echo "4. 매니페스트 수정"
    - git clone https://${GITLAB_USERNAME}:${GITLAB_PASSWORD}@gitlab.innocommerce.co.kr/framework/innog.k8s-object.git
    - cd innog.k8s-object
    - |
      sed -i "s|image: $ECR_REPO_URI.*|image: $ECR_REPO_URI:$CI_PIPELINE_ID|" deploy/$CI_COMMIT_BRANCH/$CI_PROJECT_NAME/$CI_PROJECT_NAME.yaml
    - git add deploy/$CI_COMMIT_BRANCH/$CI_PROJECT_NAME/$CI_PROJECT_NAME.yaml
    - git commit -m "Update image tag to $CI_PIPELINE_ID"
    - git pull origin master
    - git push origin master

source-build:
  stage: source-build
  image: maven:3.8.4-openjdk-17-slim
  script: *package
  tags:
    - runner-dev
  only:
    - dev

docker-deploy:
  stage: docker-deploy
  image: aaddss639/dind4pack:latest
  services:
    - name: docker:20-dind
      command: ["--tls=false"]
  needs:
    - source-build
  variables:
    DOCKER_HOST: tcp://docker:2375
    DOCKER_DRIVER: overlay2
    DOCKER_TLS_CERTDIR: ""
    DOCKER_IMAGE_NAME: "${CI_PROJECT_NAME}-${CI_COMMIT_REF_NAME}"
    ECR_REPO_URI: "ECR_REPO_URI"
  before_script:
    - aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin account.dkr.ecr.ap-northeast-2.amazonaws.com
  script: *deploy
  retry: 2
  only:
    - dev
  tags:
    - runner-dev

 

build 단계와 deploy 단계에 스크립트는 template화 하여 분리하였다. 정확히는 변수에 따라 스크립트를 메소드화했다. 환경, 브랜치에 따라 다른 변수 값이 들어갈 수 있음을 고려했다.

 

build 단계에서는 단순히 jar를 만들고 다음 step인 deploy에 전달한다. 
deploy에서는 각 단계마다 echo를 참고하면 된다. 중간 과정에 필요한 설정 파일이나 APM 도구같은 것이 있다면 S3 같은 곳에서 받아오거나 도커파일에 담아도(도커라이징) 될 것 같다.

 

지금 deploy에 job 컨테이너의 이미지는 다음과 같다.

image: aaddss639/dind4pack:latest


단순히 필요한 것만 담아서 설치하는 과정을 포함하였다.

# base image
FROM docker:20-dind

# install necessary packages
RUN apk add --no-cache curl jq python3 py3-pip && \
    pip install awscli

# set environment variables
ENV DOCKER_HOST=tcp://docker:2375
ENV DOCKER_DRIVER=overlay2
ENV DOCKER_TLS_CERTDIR=""

# entrypoint
ENTRYPOINT ["dockerd-entrypoint.sh"]
CMD []
 

python, aws-cli 설치 등의 작업들이 매번 배포때마다 실행되니 이 부분을 도커 파일에 추가하여 CI/CD 시간을 단축할 수 있다.

 

이미지 태그는 파이프라인 번호를 주었다. 메니페스트 파일을 수정하는 작업에서 git clone, edit, commit, push를 활용했는데 생각보다 배포 수가 늘어날 수록 git 충돌이 발생하였다. 

좋은 방법에 대해 고민하다가 push 이전에 pull 작업을 추가했고 혹시나 작업에 실패할 경우 retry: 2 작업을 추가하여 재시도 로직이 들어가니 git 충돌로 인한 배포 실패 확률이 0%로 줄어들었다. 서비스가 지금보다 훨씬 많아지면 이 또한 문제가 될까 싶다.

 

Gitlab-Runner의 작업은 이렇게 마무리하고 나머지는 ArgoCD가 Sync 작업으로 배포를 전담한다. Rollout/Deploymen의 배포 strategy에 맞게 적용되니 환경에 맞게 Rolling, Blue/Green, Canary 전략을 적용하면 될 거 같다.

 

ArgoCD Sync

ArgoCD에서 위처럼 매니페스트 파일의 Repo URL, PATH를 입력하면 Sync 작업을 통해 변경 분에 따라 배포가 진행된다. 

 

ArgoCD와 관련된 설치&설정은 따로 확인하도록 하자.

ArgoCD 설치/설정
https://sh970901.tistory.com/153

 

쿠버네티스[EKS] argocd 설치 및 AWS ALB 외부 노출, Slack 설정

설치는 매우 간단하다. Getting_started만 따라해도 서비스가 간단하게 뜨는 것을 확인할 수 있다.https://argo-cd.readthedocs.io/en/release-2.0/getting_started HA for productionkubectl create namespace argocdkubectl apply -n arg

sh970901.tistory.com

 

이렇게 쿠버네티스[EKS] 환경에서 구축한 CICD 파이프라인에 대한 리뷰를 남겼다. 

 

이 포스팅을 보고 모든 걸 옮기기보다 필요한 부분만 참고하도록 하자. 맨 처음 말했던 것 처럼 요구는 바뀌고 기술은 발전하고 나는 아직 모르는게 많다. 내 상황에 맞게 여러 기술들을 잘 활용하고 변경에 유연하게 구성하는 감각을 키우는게 중요한 것 같다.