속도 개선을 위한 노력 1탄: Codebuild S3 캐시 적용하기
자비스앤빌런즈의 서비스는 대부분 AWS CodePipeline을 통해 ECS 컨테이너로 배포되고 있습니다. AWS CodePipeline은 소스를 빌드하고 테스트 후 프로덕션 환경에 배포하는 과정을 자동화할 수 있는 CI/CD 도구입니다. 다른 AWS 서비스와 쉽게 연동하여 사용하기 좋은 툴이죠.
하지만 AWS CodePipeline은 배포까지 꽤 많은 시간이 소요됩니다. 빌드를 수행하는 EC2 인스턴스가 별도로 실행되기 때문에 인스턴스 구동 시간+빌드를 처리하고 서빙하는 시간이 항상 추가되기 때문이죠. 자비스앤빌런즈의 대표 서비스인 삼쩜삼도 소스 push
후 서비스 배포까지 약 8분 이상이 소요됩니다.
- 빌드 인스턴스의 초기화 시간:
- 각 빌드마다 인스턴스를 새로 프로비저닝하므로, 이 초기화 단계에 추가 시간이 소요됩니다.
- 의존성 다운로드:
- 빌드 시 필요한 모든 의존성을 외부 저장소(예: gradle.org)에서 다시 다운로드하므로, 네트워크 상태에 따라 속도가 느려질 수 있습니다.
인스턴스가 뜨고 빌드를 완료할 때까지 걸리는 시간을 기준으로 과금하는 AWS 비용 체계를 생각하면 더욱 속도 개선이 필요한 상황이었습니다. 고민이 깊어가던 중 당장 CI/CD 구조를 바꿀 수 없어 AWS가 제공하는 캐싱 기능을 적용해보면 어떨까 생각했습니다.
AWS Cache builds
AWS 캐시빌드는 Amazon S3 또는 로컬 두 유형 중 하나를 사용할 수 있습니다.
S3 캐시
buildspec
파일의 cache
블록에서 캐싱할 디렉터리를 지정할 수 있습니다. 이 방식은 빌드 도구의 의존성(Dependency)을 다운로드하는 시간을 줄이는 데 사용됩니다. 단, 의존성의 크기가 작은 경우 효과적이지만 큰 경우에는 S3에서 데이터를 가져오는 데 시간이 더 오래 걸릴 수 있습니다.
로컬 캐시
로컬 캐시는 세 가지 유형으로 나뉩니다: 소스 캐시, Docker 레이어 캐시, 커스텀 캐시입니다.
- 소스 캐시는 git 저장소의 변경 사항만을
pull
하여DOWNLOAD_SOURCE
단계의 시간을 줄일 수 있습니다. - 커스텀 캐시는
buildspec
파일에 명시된 캐시 디렉터리를 기반으로 캐시 데이터를 생성합니다. - Docker 레이어 캐시는 Docker 이미지를 빌드하는 프로젝트에 적합하며, 중복되는 빌드 단계를 생략하여 전체 빌드 시간을 효율적으로 단축할 수 있습니다.
자세한 사항은 AWS 공식문서를 참고해주세요.
소스 캐시는 git의 metadata를 캐싱하여 소스 코드의 변경사항을 다운받는 형식인데, CodePipeline과 연계하여 빌드를 수행하는 경우 S3를 통해 소스를 다운로드 하기 때문에 빌드 및 배포를 CodePipeline으로 수행하는 자비스앤빌런즈 서비스는 해당 옵션을 사용할 수 없었습니다. Docker 레이어 캐시는 ECR 빌드 이미지를 미리 pull
하고 실행해야 하며 동일한 빌드 호스트에 대해서만 적용되므로 빌드를 자주 하는 경우에 유리하죠. 이러한 내용을 감안해서 자비스앤빌런즈 서비스에 범용적으로 적용 가능하고, 즉각적으로 효과를 테스트할 수 있는 S3 캐시 빌드를 채택하게 되었습니다.
AWS Codebuild가 S3 빌드 캐싱하는 방법
CI 환경에서 캐시란, 주로 프로젝트에 필요한 의존성 파일을 캐시에 저장한 후 빌드할 때 꺼내쓰는 것을 말하죠. AWS Codebuild S3 캐싱 역시 동일합니다.
- 신규 배포를 진행합니다.
- Codebuild가 S3 버킷에서 캐싱된 파일을 다운로드하여 빌드를 수행합니다. (예:
Gradle jib
등) - 새로운 파일이 필요하면, gradle.org에서 필요한 파일을 다운로드한 후 함께 빌드를 수행합니다.
- 빌드 완료 후,
buildspec
파일의cache
섹션에 지정된 경로에 있는 파일들이 다시 S3에 업로드 됩니다.
어떻게 적용하나요?
1-1. Terraform 사용 시
resource "aws_codebuild_project"
의 cache
블록에서 설정 가능합니다.
cache {
location = "BUCKET_NAME/PRE_FIX"
modes = []
type = "S3"
}
자세한 사항은 Terraform 공식문서를 참고해주세요.
1-2. AWS Console에서 설정하기
AWS Console 로그인 후 Codebuild 서비스에서 프로젝트를 선택, 편집 버튼을 클릭합니다.
a. 캐시 유형: Amazon S3 선택합니다.
b. 캐시 파일을 저장할 버킷을 선택합니다.
- 암호화 키: 기본적으로 S3에 대한 AWS 관리형 키가 사용되지만, 필요 시 AWS KMS 고객 관리형 키를 선택할 수 있습니다.
- 캐시 버킷: 캐시 파일이 저장될 S3 버킷을 선택합니다.
- 캐시 경로 접두사: 캐시 파일을 저장할 경로를 지정할 수 있습니다.
- 캐시 수명 주기(일): 캐시 파일의 보관 기간을 설정할 수 있습니다.
2. buildspec 설정
캐시 유형과 저장소 경로 설정이 완료되면 buildspec
에서 캐싱할 파일의 경로를 지정하여 배포합니다.
version: 0.2
cache:
paths:
- '/root/.gradle/caches/**/*'
- '/root/.gradle/wrapper/**/*'
- '.gradle/**/*'
테스트 결과
초기 캐싱 과정에서는 의존성 파일을 S3에 업로드 하면서 약 3~40초의 추가 지연이 발생했지만, 이후 캐시가 히트(hit)되면서 빌드 속도가 눈에 띄게 단축되었습니다. 특히, 캐시가 제대로 활용된 후에는 최소 38%에서 최대 61%까지 빌드 시간이 감소하는 것을 확인할 수 있었습니다.
아래는 각 프로젝트의 빌드 시간 비교 결과입니다.
대상 | 적용 전 | 1st | 2nd |
---|---|---|---|
A | 5m 12s | 3m 4s | 3m 14s |
B | 5m 5s | 3m 17s | 3m 0s |
C | 6m 36s | 3m 53s | 3m 44s |
D | 4m 52s | 1m 54s | 1m 53s |
E | 4m 49s | 1m 37s | 1m 51s |
참고: cache log
## S3 캐시 Download
[Container] 2024/05/24 05:24:09.184274 Expanded cache path /root/.gradle/caches/**/*
[Container] 2024/05/24 05:24:09.184274 Expanded cache path /root/.gradle/wrapper/**/*
[Container] 2024/05/24 05:24:09.184274 Expanded cache path .gradle/**/*
[Container] 2024/05/24 05:24:09.190502 Downloading S3 cache...
## S3 캐시 Upload
[Container] 2024/05/24 05:25:33.503259 Entering phase POST_BUILD
[Container] 2024/05/24 05:25:33.503617 Uploading S3 cache...
[Container] 2024/05/24 05:25:49.273159 Phase complete: POST_BUILD State: SUCCEEDED
[Container] 2024/05/24 05:25:49.273191 Phase context status code: Message:
BUILD SUCCESSFUL in 1m 18s
25 actionable tasks: 24 executed, 1 up-to-date
Codebuild vs. S3 cache build 비용 비교
빌드 시간을 단축하는 것만으로도 효율성이 대폭 향상되었지만, 이를 통해 비용 절감효과도 확인할 수 있었습니다.
- 기존 빌드: 캐시를 사용하지 않는 경우, 빌드 시간에 따른 리소스 사용이 많아지며 비용이 상승합니다.
- S3 캐시 빌드: 캐싱을 통해 빌드 시간 단축 + S3의 저렴한 저장 비용으로 기존 대비 약 8.5% 절감 효과를 확인할 수 있습니다.
1. CodeBuild 실행 비용
빌드 실행 비용은 인스턴스 유형과 실행 시간에 따라 결정됩니다. 여기에서는 general1.medium
유형을 기준으로 했습니다.
- 요금: 분당 USD 0.01
- 평균 빌드 시간: 7분
- 총 비용: 7분 × USD 0.01/분 = USD 0.07/빌드(1회 기준)
- 월 비용: 30회 × USD 0.07 = USD 2.10/월
캐싱 적용 후 빌드 시간이 단축됨에 따라 비용도 자연스럽게 감소합니다.
2. S3 저장 비용
캐싱된 파일은 S3에 저장됩니다. 캐시 파일 크기를 1GB로 가정할 때, 저장 비용은 다음과 같습니다:
- S3 Standard (서울 리전):
- 1GB당 USD 0.025/월
- 월간 저장 비용: USD 0.025
3. S3 요청 비용
모든 스토리지 클래스에 대한 LIST 요청에는 S3 Standard PUT, COPY 및 POST 요청과 같은 요금이 부과됩니다
- PUT 요청 비용(1GB당): 1,000 x 0.0045 = USD 0.0045
- GET 요청 비용(1GB당): 1,000 x 0.00035 = USD 0.00035
- 빌드당 요청 비용: PUT + GET = USD 0.00485/빌드
- 월 비용: 30회 x 0.00485 = USD 0.1455
4. S3 데이터 전송 비용
S3에서 CodeBuild로 데이터를 다운로드하는 비용은 동일 리전 내 전송은 무료입니다. 하지만, 인터넷 송신이 발생하는 경우 다음과 같은 요금이 부과됩니다:
- 인터넷 송신: 10TB 이하 USD 0.126/GB
일반적으로 리전 내 데이터 전송만 수행되기 때문에 추가 비용이 발생하지 않습니다.
자세한 사항은 AWS S3 요금 페이지를 참고해주세요.
항목 | 기존 빌드 (캐싱 X) | S3 캐시 빌드 |
---|---|---|
CodeBuild 실행 시간 (general1.medium) | USD 2.10/월 (7분/빌드) | USD 1.75/월 (5분/빌드) |
S3 저장 비용(1GB) | N/A | USD 0.025 |
S3 요청 비용 | N/A | USD 0.1455 |
S3 전송 비용 | N/A | 무료 (리전 내) |
총 비용 | USD 2.10 | USD 1.9205 |
Codebuild 실행 비용, S3 저장, 요청 비용 등 빌드와 관련된 다양한 요소들을 최대한 세밀하게 고려하여 실제 비용 절감 효과를 추정해 봤습니다. 비록 초기에는 추가적인 요청 비용과 설정 작업이 발생하지만, 속도와 비용 두 가지를 모두 개선할 수 있는 전략이라는 점에서 S3 캐싱은 AWS CI/CD 파이프라인에서 반드시 고려해야 할 최적화 기법이라 볼 수 있습니다.
S3 Cache clean
AWS CodeBuild에서 S3 캐싱은 빌드 시간 단축과 비용 효율성을 제공하지만, 캐시된 파일이 오래된 버전일 경우 새로운 빌드 환경에 적합하지 않은 파일이 재사용되어 빌드 실패 또는 예상하지 못한 결과를 유발할 수 있습니다. 이를 위해 캐시 클린을 해야하는데, 현재 개발자는 S3 버킷에 접근할 수 있는 권한이 없어 캐시된 파일을 삭제하거나 관리하기 위해 매번 인프라 팀에 요청해야하는 번거로움이 있었습니다.
이를 해결하기 위해 Slack 명령어를 활용한 캐시 클린 자동화 기능을 구현했습니다.
- 객체 조회:
- 개발자는 Slack 명령어로 특정 환경(dev, stg, prd)에 해당하는 S3 버킷 내 객체를 조회할 수 있습니다.
- 조회 결과는 폴더와 파일명의 정보로 구성되어 Slack 메시지로 반환됩니다.
- 예를 들어,
"/s3 list dev"
명령어를 입력하면 dev 환경의 S3 버킷에 저장된 객체 목록을 확인할 수 있습니다.
- 객체 삭제:
- Slack 명령어를 통해 특정 객체를 선택하여 삭제할 수 있습니다.
- 예를 들어,
"/s3 delete dev folder_name"
명령어를 사용하면 dev 환경에서 지정된 폴더의 모든 객체가 삭제됩니다. - 삭제 작업 완료 후에는 삭제된 객체 정보와 함께 성공 여부가 Slack 메시지로 반환됩니다.
구성 방안
- 중앙 관리 계정: API Gateway와 Lambda를 생성 및 관리하는 계정으로, dev, stg, prd 계정의 버킷에 접근할 수 있도록 IAM 역할을 사용하여 권한을 부여합니다.
- Cross-Account IAM Role: 각 환경(dev, stg, prd)의 S3 버킷에 Lambda가 접근할 수 있도록 role을 생성하고, Lambda가 이 역할을 사용할 수 있도록 설정합니다. Lambda는 해당 역할을 통해 필요한 권한만 부여받아 작업을 수행하므로, S3 데이터의 무분별한 접근을 방지합니다.
- 제한된 Slack 채널: S3 Cache clean 명령어는 특정 채널에서만 실행 가능하므로, 민감한 관리 작업이 무분별하게 노출되지 않습니다.
- 명령어 실행자 및 내역 로깅: 모든 명령 실행은 실행자의 Slack ID, 명령어 내용, 실행 시간과 함께 로깅되어 보안 감사와 문제 분석에 활용됩니다.
마치며
이번 글에서는 AWS CodeBuild에서의 S3 캐싱 적용을 통한 빌드 시간 단축과 관리 효율성을 높이는 여정을 소개했습니다. 간단한 설정만으로도 빌드 시간 단축과 비용 절감 효과를 동시에 달성했네요.
다음 글에서는 Github Actions로 CI/CD 파이프라인을 전환하며 속도와 업무 생산성을 더욱 강화한 사례를 공유할 예정입니다.
- 어떻게 Github Actions로의 전환이 이루어졌는지,
- 기존 파이프라인 대비 어떤 개선효과가 있었는지,
- 그리고 전환 과정에서 마주했던 도전 과제와 그 해결 방법까지.
속도 개선을 위한 노력 2탄에서는 보다 구체적이고 실질적인 전환 사례를 다룰 예정이니 많은 관심 부탁드립니다. 감사합니다! 🚀
글 |
박은영
디자인 |
박서영
본 콘텐츠의 저작권은 (주)자비스앤빌런즈에게 있으며, 본 컨텐츠에 대한 무단 전재 및 재배포를 금지합니다