안녕하세요 현재 사내에서 유일한 백엔드 개발자로 일하고 있는 제하쓰입니다!
저희 회사는 API 서버를 AWS 클라우드 환경에서 구축하여 사용하고 있는데요. 제가 들어오기 전에 구축한 서버는 AWS Beanstalk에 개발자가 직접 FileZilla를 이용해서 코드를 업데이트하는 방식이었습니다. 제가 들어오고 나서 신규 프로젝트를 위해서 신규 서버 아키텍처를 구축할 때 이러한 불편함을 개선하고자 프로젝트에 CI/CD를 도입하고자 했습니다.
저희 회사에서는 백엔드 개발자가 단 2명뿐이라 자동화가 절실한 상황이었습니다. 코드 변경 사항이 파이프라인의 이전 단계(CI)를 모두 성공적으로 통과하면 개발자의 수동 개입 없이 프로덕션에 자동으로 배포되는 환경을 구축하는 게 저희의 최종 목표였습니다.
실제 서비스 환경에서 어떤 CI/CD 도구들을 사용하는지 한번 알아보겠습니다.
CI/CD 도구의 종류와 비교
Teamcity 블로그에서 가져온 CI 도구 사용 선호도 조사입니다. 역시 Github Actions와 Jenkins를 가장 많이 쓰는 걸 볼 수 있습니다. Azure Devops는 예외네요? 은근 순위가 높습니다.. 해외는 Azure 클라우드도 많아 사용하나 봅니다.
저희 환경에서 쓸만한 도구들을 좀 더 자세히 보겠습니다.
Jenkins | Github Actions | GitLab | AWS CodePipeline | |
서버 | 별도의 서버 필요 | 클라우드로 동작 | 클라우드 or 설치형 | 클라우드로 동작 |
비용 | Jenkins 툴은 무료, 하지만 Jenkins를 실행하는 인프라 비용 발생 |
Private Repository는 한달에 500MB, 2000분 까지 무료사용 초과되는 분마다 비용 지불 | 무료로 400분의 CI/CD 사용 지원, 추가 이용을 위해서는 유료 라이선스 필요 | Pricing per Pipeline: Additional Storage Costs: |
OS | 모든 OS 호환 가능 | 모든 OS 호환 가능 | 모든 OS 호환 가능 | 모든 OS 호환 가능 |
플러그인 | 약 1400개의 플러그인 존재 | jenkins에 비해 적다 스크립트로 플러그인 추가 가능 | jenkins에 비해 적다. | AWS CodePipeline은 플러그인 시스템 대신 다양한 AWS 서비스와의 통합을 통해 기능을 확장합니다. |
동기/비동기 | 젠킨스 파이프라인을 통한 비동기 처리 가능 | 비동기 CI/CD 가능 | 비동기 처리 가능 | 비동기 처리가능, Step Functions 를 이용시 복잡한 워크플로우 처리 가능 |
사용성 | GUI라 친근하나 초기 설정이 어렵다 | jenkins에 비해 적다. | jenkins에 비해 적다. | AWS Management Console을 통해 사용이 비교적 쉬우며, AWS CLI 및 SDK를 통해 자동화 및 설정이 가능합니다. 초기 설정이 간단하다. |
문서화 | 문서가 다양하다 | jenkins에 비해 적다. | jenkins에 비해 적다. | AWS의 광범위한 문서화와 튜토리얼, 그리고 AWS Support를 통한 지원이 제공 |
REST API | REST API 지원 | Github API 지원 | REST API 지원 | AWS CodePipeline은 REST API를 지원 |
장점 | ▶자동화 테스트 수행. ▶정적 코드 분석에 의한 코딩 규약 준수여부 체크. ▶프로젝트 표준 컴파일 환경에서의 컴파일 오류 검출. ▶프로파일링 툴을 이용한 소스 변경에 따른 성능 변화 감시. ▶빌드 파이프라인의 구성을 간단히 할 수 있음. ▶각종 배치 작업의 간략화. |
▶Github 마켓 플레이스를 통한 Workflow 복제가 용이. ▶Github에 친숙한 개발자들에게 접근성이 좋음. ▶실행 및 디버깅이 쉬움. ▶Github API를 통해 쉽게 액세스 가능. ▶서버를 직접 관리하지 않아도 되며, Github 페이지에서 모든 과정을 확인 가능. |
▶Auto-Scaling CI Runner를 제공. ▶다른 Tool에 비해 UI가 깔끔. 모바일 Web, App으로도 사용 가능. ▶오픈 소스 그룹이 활발해서 주기적인 업데이트가 있음. ▶파이프라인 내의 모든 Job들이 독립적. |
▶자동화된 배포: CI/CD 파이프라인을 통해 지속적인 배포를 자동화할 수 있다. ▶AWS 서비스와의 통합: 다양한 AWS 서비스와 원활하게 통합. ▶유연성: 다양한 빌드, 테스트, 배포 도구와의 통합을 지원. ▶확장성: AWS 인프라의 확장성을 활용하여 큰 규모의 프로젝트도 처리할 수 있다. ▶모니터링: AWS CloudWatch와 통합되어 파이프라인 실행 상태를 모니터링할 수 있다. |
단점 | ⛔프로젝트의 규모가 작은 경우, 설정하는데 리소스 낭비가 발생. ⛔ 호스팅을 직접해야하기 때문에 서버 운영 및 관리 비용 발생. ⛔ 플러그인을 최신 상태로 유지해야하며 업데이트 하지 않을 경우 장애가 발생 할 수 있음. |
⛔ Public Repository에서는 무료로 사용 가능하지만, Private Repository에서는 요금이 발생. ⛔ 참고할만한 문서가 비교적 부족. ⛔ Workflow에서 단일 작업만 다시 실행할 수 없음. |
⛔ Push, Pull 수행 속도가 Github보다 느림. ⛔ 모든 job에 대해 artifact를 정의 및 업로드/다운로드 해야함. |
⛔ 비용: 각 파이프라인과 스토리지 사용량에 따라 비용이 증가할 수 있습니다. ⛔ 무료 요금제가 없기 때문에 소규모 프로젝트에는 비용 부담이 될 수 있다. ⛔ 복잡성: AWS 서비스에 익숙하지 않은 경우 초기 설정 및 관리를 이해하는 데 시간이 걸릴 수 있다. ⛔ 플러그인 부족: Jenkins와 같은 많은 수의 플러그인은 없지만, AWS 서비스와의 통합으로 이를 보완할 수 있다. |
총평 | ✅새로 생긴 CI툴에 비해 초기 설정이 어렵다는 단점이 있지만 , 많은 플러그인과 비용이 무료라는 이점들이 있기에 대규모 프로젝트를 운영함에 있어서 적합하다. |
✅ 최신 CI 툴이며 Github에서 사용해 친숙하며, 별도의 설치 없이 클라우드에서 관리를 하기 때문에 편리하다는 장점이 있지만, IIS 서버를 기반으로 한 CD가 불가능 하다는 단점이 있다. | ✅ Github에서 기존에 제공하던 형상관리 기능과 더불어 CI 기능까지 제공하지만, GitLab과 관련된 참고문서가 적고 기존 Repository를 옮겨야 한다는 단점이 있다. |
✅ AWS CodePipeline은 클라우드 기반으로 설정이 간편하고, AWS 서비스와의 통합성이 뛰어나며, 확장성 및 유연성이 높다. 대규모 프로젝트와 복잡한 CI/CD 워크플로우에 적합. |
여기서 후보군을 3개 정도로 압축할 수 있는데요, Jenkins, Github Actions, AWS CodePipeline 중에 하나를 선택할 수 있을 것 같습니다. 먼저 Jenkins는 많은 예제와 사용 사례 + 제가 학생 때 사용해 본 경험이 있습니다. 다만 Jenkins를 실행하는데 별도 인프라가 필요하고 저희는 AWS를 사용 중이기 때문에 제외하였습니다.
Github Actions는 많은 조직들이 도입해서 사용하는 도구인데 저희 같은 경우 Github를 무료 버전으로 사용하고 있어서 코드 repository를 Public으로 전환해야 하는 이슈가 생깁니다.
마지막으로 AWS CodePipeline은 클라우드 기반에 AWS 서비스와 통합성이 좋습니다. 저희 서버는 AWS ECS에서 동작하고 있고 이미 AWS 통합 빌링으로 결제 품의서와 지출 결의서가 올라가고 있어 별도 결제 작업이 필요 없어 바로 적용해 볼 수 있는 게 가장 큰 요인으로 작용했습니다.
CodePipeline으로 CI/CD를 구축해 보겠습니다.
AWS CodePipeline 적용하기
AWS CodePipeline은 크게 3가지 스테이지로 구분할 수 있습니다.
소스 스테이지
- 메인 코드 Repository의 Main Branch에 Push 또는 Merge를 트리거로 하여 CI/CD 시작합니다.
- Github v2 환경이며 AWS Connector 앱을 Repository에 설치합니다.
빌드 스테이지
- AWS CodeBuild에 정의된 buildspec.yml을 따라서 빌드합니다.
- 도커로 이미지를 빌드하여 AWS Elastic Container Registry에 푸시한다. 이때 이미지의 태그는 커밋 아이디로 지정한다.
- 생성된 이미지를 taskdef.json의 <IMAGE1_NAME> 자리에 sed -i 명령어를 이용해 바꿔치기한다. 동적으로 이미지 지정하는 기능을 ECS Blue/Green 배포의 경우 사용할 수 없어 이런 식으로 구현하였다.
- 출력 아티팩트는 S3에 저장한다.
배포 스테이지
- 출력 아티팩트에서 생성된 imagedefinitions.json, appspec.yml, taskdef.json을 이용해 AWS ECR에서 도커 이미지를 가져와 컨테이너로 배포한다.
- 배포 시 트래픽을 한 번에 이동시킨다. 트래픽 이동 완료 후 원본 태스크는 1시간 동안 대기후 삭제한다.
이제 실제 AWS 환경에서 구축하겠습니다.
실제 환경에 적용
글을 쓰려고 보니까 제가 구축했을 때 하고 GUI랑 옵션이 바뀌었더라고요, 그때는 사용자 지정 파이프라인 빌드밖에 없었는데 여러 탬플릿들이 추가된 모습입니다. 당황(?)하지 말고 사용자 지정 파이프라인 빌드를 선택해 주세요.
파이프라인 이름을 작성해 주시고 실행 모드는 대기됨으로 선택해 주세요.
CodePipeline은 선택할 수 있는 실행 모드를 제공합니다.
- 대체됨 - 최근 실행이 이전 실행보다 앞설 수 있습니다. 이 값이 기본값입니다.
- 대기됨(유형 V2만 해당) - 실행은 대기열에 있는 순서대로 하나씩 처리됩니다.
- 병렬(유형 V2만 해당) - 실행은 다른 실행이 완료될 때까지 기다리지 않고 시작하거나 종료됩니다.
기존 역할이 없다면 새 서비스 역할을 생성하시고 다음을 눌러주세요.
소스 공급자는 코드를 가져올 공간입니다. S3, ECR, Bitbucket 등등 환경에 따라 선택해 주시면 되는데 저희는 Github를 선택하겠습니다. 기존에 구성된 연결이 없으시다면 Github에 연결 버튼을 통해 Repository에 AWS Connector 앱을 설치하게 됩니다. 리포지토리 이름과 코드를 가져올 브랜치를 선택하시고 밑에 Webhook 이벤트는 꼭 체크해 주셔야 파이프라인이 브랜치가 Push 또는 PR Merge에서 시작합니다.
빌드 스테이지에서는 가져온 소스 코드를 실행할 명령어 스크립트를 작성하는 곳입니다.
저희는 기타 빌드 공급자에서 AWS CodeBuild를 선택하겠습니다. 생성된 AWS CodeBuild가 없다면 프로젝트 생성을 클릭하여 새로운 CodeBuild를 작성하세요.
CodeBuild를 생성할 때 프로젝트 이름을 설정하시고 밑으로 스크롤해보시면 다음과 같이 Build명령어를 작성할 수 있는 공간이 있습니다. 편집기로 전환 버튼을 누르시면 아래처럼 작성할 수 있는 공간이 생깁니다.
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region us-east-1 | docker login <AWS ECR Docker 로그인>
build:
commands:
- echo Building the Docker image...
- docker build -t backend-fastapi .
- COMMIT_HASH=$(git rev-parse --short HEAD)
- echo Commit hash is $COMMIT_HASH
- docker tag backend-fastapi:latest 2534750000.dkr.ecr.us-east-1.amazonaws.com/backend-fastapi:$COMMIT_HASH
post_build:
commands:
- echo Pushing the Docker image...
- docker push 2534750000.dkr.ecr.us-east-1.amazonaws.com/backend-fastapi:$COMMIT_HASH
- echo Writing imageDetail file...
- printf '[{"name":"%s","imageUri":"%s"}]' "fastapi" 2534750000.dkr.ecr.us-east-1.amazonaws.com/backend-fastapi:$COMMIT_HASH > imagedefinitions.json
- echo Replacing image name in taskdef.json...
- sed -i "s|<IMAGE1_NAME>|2534750000.dkr.ecr.us-east-1.amazonaws.com/nueyne-backend-fastapi:$COMMIT_HASH|" taskdef.json
artifacts:
files:
- imagedefinitions.json
- taskdef.json
- appspec.yml
- 도커로 이미지를 빌드하여 AWS Elastic Container Registry에 푸시한다. 이때 이미지의 태그는 커밋 아이디로 지정한다.
- 생성된 이미지를 taskdef.json의 <IMAGE1_NAME> 자리에 sed -i 명령어를 이용해 바꿔치기한다. 동적으로 이미지 지정하는 기능을 ECS Blue/Green 배포의 경우 사용할 수 없어 이런 식으로 구현하였다.
- 출력 아티팩트는 S3에 저장한다.
테스트 스테이지는 최종 배포전에 빌드된 코드를 미리 테스트해볼 수 있는 스테이지입니다. 현재는 건너뛰기하겠습니다.
Amazon ECS(Blue/Green)을 선택해 줍니다. 입력아티팩트가 BuildArtifact로 정의돼있는지 확인해 주시고 CodeDeploy 애플리케이션이 없다면 새로 생성해 주시면 됩니다. 배포그룹도 없으시다면 CodeDeploy에서 새로 생성해 주세요.
Amazon ECS 작업정의와 Codeploy AppSpec파일이 BuildArtifact로 잘 지정이 되었다면 배포 스테이지 설정이 완료됩니다.
이제 Github main 브랜치의 코드가 업데이트될 때마다 자동으로 배포가 진행되게 됩니다.
회고
운영 서버의 경우 무중단 배포가 중요하여 ECS Blue/Green으로 배포를 진행하였는데, 생각보다 자료도 많이 없고 거의 머리를 박아가면서 했던 기억이 나네요. 특히 저 빌드아티팩트의 결과물인 taskdef.json을 따로 프로젝트 root에 작성해야 하는 거에서 많이 헤맸었습니다. 운영 환경 구성에는 대부분 콘솔 GUI로 구성하였는데 Terraform과 같은 IaaC 서비스의 필요성을 새삼 다시 한번 느꼈습니다. 인프라단에서 구성에 실수한 부분을 찾는 게 상당히 번거롭고 어려운 작업이었습니다.
궁금하신 점이나 피드백은 언제든지 환영입니다.
감사합니다.
'개발 > 클라우드' 카테고리의 다른 글
AWS Certified Developer - Associate 합격 후기 및 꿀팁 (0) | 2025.04.13 |
---|---|
네이버 클라우드를 이용한 카카오 알림톡 전송하기 (0) | 2025.03.18 |